diff --git a/lib/browser-sync.js b/lib/browser-sync.js index 16d88e762..20819c095 100644 --- a/lib/browser-sync.js +++ b/lib/browser-sync.js @@ -613,16 +613,25 @@ BrowserSync.prototype.doFileReload = function (data) { ); bs._reloadTimer = setTimeout(function () { + if (willReload) { - bs.io.sockets.emit("browser:reload"); + if (!bs._reloadDebounced) { + bs._reloadDebounced = setTimeout(function () { + bs._reloadDebounced = false; + }, bs.options.get("reloadDebounce")); + bs.io.sockets.emit("browser:reload"); + } } else { bs._reloadQueue.forEach(function (item) { bs.io.sockets.emit("file:reload", item); }); } + clearTimeout(bs._reloadTimer); + bs._reloadTimer = undefined; bs._reloadQueue = []; + }, bs.options.get("reloadDelay")); }; diff --git a/lib/cli/opts.start.json b/lib/cli/opts.start.json index 859d808ea..68c27f6b6 100644 --- a/lib/cli/opts.start.json +++ b/lib/cli/opts.start.json @@ -17,6 +17,8 @@ "host": "Specify a hostname to use", "logLevel": "Set the logger output level (silent, info or debug)", "port": "Specify a port to use", + "reload-delay": "Time in milliseconds to delay the reload event following file changes", + "reload-debounce": "Time in milliseconds to delay the reload event following file changes", "ui-port": "Specify a port for the UI to use", "no-notify": "Disable the notify element in browsers", "no-open": "Don't open a new browser window", diff --git a/lib/default-config.js b/lib/default-config.js index 03862f306..148842a7a 100644 --- a/lib/default-config.js +++ b/lib/default-config.js @@ -262,12 +262,24 @@ module.exports = { scrollThrottle: 0, /** + * Time, in milliseconds, to wait before + * instruction browser to reload/inject following a + * file change event * @property reloadDelay * @type Number * @default 0 */ reloadDelay: 0, + /** + * Restrict the frequency in which browser:reload events + * can be emitted to connected clients + * @property reloadDebounce + * @type Number + * @default 0 + */ + reloadDebounce: 0, + /** * User provided plugins * @property plugins diff --git a/test/specs/files/files.watching.debounce.js b/test/specs/files/files.watching.debounce.js new file mode 100644 index 000000000..49bc46f2d --- /dev/null +++ b/test/specs/files/files.watching.debounce.js @@ -0,0 +1,87 @@ +"use strict"; + +var browserSync = require("../../../"); +var sinon = require("sinon"); +var assert = require("chai").assert; + +describe("File Watcher Module - reloadDebounce = 0", function () { + var bs, clock, stub, data; + before(function (done) { + browserSync.reset(); + var config = { + server: "test/fixtures", + open: false, + logLevel: "silent", + reloadDebounce: 0, + online: false + }; + clock = sinon.useFakeTimers(); + bs = browserSync(config, function () { + stub = sinon.stub(bs.io.sockets, "emit"); + done(); + }).instance; + }); + beforeEach(function () { + data = {path: "/index.html"}; + clock.now = 0; + }); + after(function () { + clock.restore(); + bs.io.sockets.emit.restore(); + bs.cleanup(); + }); + afterEach(function () { + stub.reset(); + }); + it("Fires as fast as possible with no debounce", function (done) { + bs.events.emit("file:reload", data); + clock.tick(); + bs.events.emit("file:reload", data); + clock.tick(); + assert.isTrue(stub.withArgs("browser:reload").calledTwice); // should be called for each + done(); + }); +}); + +describe("File Watcher Module - reloadDebounce = 1000", function () { + var bs, clock, stub, data; + before(function (done) { + browserSync.reset(); + var config = { + server: "test/fixtures", + open: false, + logLevel: "silent", + reloadDebounce: 1000, + online: false + }; + clock = sinon.useFakeTimers(); + bs = browserSync(config, function () { + stub = sinon.stub(bs.io.sockets, "emit"); + done(); + }).instance; + }); + beforeEach(function () { + data = {path: "/index.html"}; + clock.now = 0; + }); + after(function () { + clock.restore(); + bs.io.sockets.emit.restore(); + bs.cleanup(); + }); + afterEach(function () { + stub.reset(); + }); + it("limits events to a 1000 interval", function (done) { + bs.events.emit("file:reload", data); + clock.tick(50); + bs.events.emit("file:reload", data); + clock.tick(50); + bs.events.emit("file:reload", data); + clock.tick(50); + bs.events.emit("file:reload", data); + clock.tick(1000); + assert.isTrue(stub.withArgs("browser:reload").calledOnce); + done(); + }); +});