Skip to content

Commit 2282cad

Browse files
committed
fix(files): implement a more robust (correct) debounce, along with throttle & delay - re: #982 #1121
1 parent 66be45c commit 2282cad

10 files changed

Lines changed: 424 additions & 386 deletions

lib/browser-sync.js

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -648,39 +648,21 @@ BrowserSync.prototype.doFileReload = function (data) {
648648

649649
var bs = this;
650650

651-
bs._reloadQueue = bs._reloadQueue || [];
652-
bs._reloadQueue.push(data);
653-
654-
if (bs._reloadTimer) {
655-
return;
656-
}
657-
658651
var willReload = utils.willCauseReload(
659-
bs._reloadQueue.map(function (item) { return item.path; }),
652+
[data.path],
660653
bs.options.get("injectFileTypes").toJS()
661654
);
662655

663-
bs._reloadTimer = setTimeout(function () {
664-
665-
if (willReload) {
666-
if (!bs._reloadDebounced) {
667-
bs._reloadDebounced = setTimeout(function () {
668-
bs._reloadDebounced = false;
669-
}, bs.options.get("reloadDebounce"));
670-
bs.io.sockets.emit("browser:reload");
671-
}
672-
} else {
673-
bs._reloadQueue.forEach(function (item) {
674-
bs.io.sockets.emit("file:reload", item);
675-
});
676-
}
677-
678-
clearTimeout(bs._reloadTimer);
679-
680-
bs._reloadTimer = undefined;
681-
bs._reloadQueue = [];
656+
/**
657+
* If the current item will cause the browser
658+
* to reload, fire the correct
659+
*/
660+
if (willReload) {
661+
bs.io.sockets.emit("browser:reload");
662+
return;
663+
}
682664

683-
}, bs.options.get("reloadDelay"));
665+
bs.io.sockets.emit("file:reload", data);
684666
};
685667

686668
/**

lib/default-config.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,25 @@ module.exports = {
326326
reloadDelay: 0,
327327

328328
/**
329-
* Restrict the frequency in which browser:reload events
330-
* can be emitted to connected clients
329+
* Wait for a specified window of event-silence before
330+
* sending any reload events.
331331
* @property reloadDebounce
332332
* @type Number
333333
* @default 0
334334
* @since 2.6.0
335335
*/
336336
reloadDebounce: 0,
337337

338+
/**
339+
* Emit only the first event during sequential time windows
340+
* of a specified duration.
341+
* @property reloadThrottle
342+
* @type Number
343+
* @default 0
344+
* @since 2.13.0
345+
*/
346+
reloadThrottle: 0,
347+
338348
/**
339349
* User provided plugins
340350
* @property plugins

lib/file-watcher.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"use strict";
22

33
var _ = require("../lodash.custom");
4+
var utils = require("./utils");
5+
var Rx = require("rx/dist/rx.all.js");
46

57
/**
68
* Plugin interface
@@ -10,6 +12,17 @@ module.exports.plugin = function (bs) {
1012

1113
var options = bs.options;
1214
var emitter = bs.emitter;
15+
var subject = new Rx.Subject();
16+
var sub$ = getObservable(subject, bs.options);
17+
18+
var subscription = sub$.subscribe(function (value) {
19+
emitter.emit("file:changed", value);
20+
});
21+
22+
bs.registerCleanupTask(function () {
23+
subscription.dispose();
24+
subject.dispose();
25+
});
1326

1427
var defaultWatchOptions = options.get("watchOptions").toJS();
1528

@@ -21,7 +34,7 @@ module.exports.plugin = function (bs) {
2134
* @param path
2235
*/
2336
var fn = function (event, path) {
24-
emitter.emit("file:changed", {
37+
subject.onNext({
2538
event: event,
2639
path: path,
2740
namespace: namespace
@@ -82,3 +95,38 @@ function watch (patterns, opts, cb) {
8295
}
8396

8497
module.exports.watch = watch;
98+
99+
/**
100+
* Apply debounce, throttle or delay operators
101+
* to the default stream of events
102+
* @param subject
103+
* @param options
104+
* @returns {*|Observable.<T>}
105+
*/
106+
function getObservable(subject, options) {
107+
108+
var globalItems = [
109+
{
110+
option: "reloadDebounce",
111+
fnName: "debounce"
112+
},
113+
{
114+
option: "reloadThrottle",
115+
fnName: "throttle"
116+
},
117+
{
118+
option: "reloadDelay",
119+
fnName: "delay"
120+
}
121+
];
122+
123+
var scheduler = options.getIn(["debug", "scheduler"]);
124+
125+
return globalItems.reduce(function(subject, item) {
126+
var value = options.get(item.option);
127+
if (value > 0) {
128+
return subject[item.fnName].call(subject, value, scheduler);
129+
}
130+
return subject;
131+
}, subject);
132+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"portscanner": "^1.0.0",
5151
"qs": "6.2.0",
5252
"resp-modifier": "6.0.1",
53+
"rx": "4.1.0",
5354
"serve-index": "1.7.3",
5455
"serve-static": "1.10.2",
5556
"socket.io": "1.4.6",

test/specs/e2e/files/e2e.file.watching.js

Lines changed: 40 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,105 +4,65 @@ var browserSync = require("../../../../");
44

55
var path = require("path");
66
var assert = require("chai").assert;
7-
87
var outpath = path.join(__dirname, "../../fixtures");
98

10-
describe("file-watching", function () {
11-
12-
describe("E2E Adding namespaced watchers", function () {
13-
14-
var instance, file;
15-
16-
before(function (done) {
9+
describe("file-watching (2)", function () {
1710

18-
browserSync.reset();
11+
it("Watches files with no namespace", function (done) {
12+
browserSync.reset();
1913

20-
file = path.join(outpath, "watch-func.txt");
21-
22-
var config = {
23-
files: file,
24-
logLevel: "silent"
25-
};
26-
27-
instance = browserSync(config, done).instance;
28-
});
14+
var file = path.join(outpath, "watch-func.txt");
2915

30-
after(function () {
31-
instance.cleanup();
32-
});
16+
var config = {
17+
files: file,
18+
logLevel: "silent"
19+
};
3320

34-
it("Watches files with no namespace", function (done) {
21+
browserSync(config, function (err, bs) {
3522

36-
assert.ok(instance.watchers.core.watchers);
37-
assert.equal(instance.watchers.core.watchers.length, 1);
23+
assert.ok(bs.watchers.core.watchers);
24+
assert.equal(bs.watchers.core.watchers.length, 1);
25+
bs.cleanup();
3826
done();
3927
});
4028
});
4129

42-
describe("E2E Adding namespaced watchers", function () {
43-
44-
var instance, file;
45-
46-
before(function (done) {
47-
48-
browserSync.reset();
30+
it("Watches files when multi given", function (done) {
31+
browserSync.reset();
4932

50-
file = path.join(outpath, "watch-func.txt");
51-
52-
var config = {
53-
files: "*.html",
54-
logLevel: "silent"
55-
};
56-
57-
instance = browserSync(config, done).instance;
58-
});
33+
var config = {
34+
files: "*.html",
35+
logLevel: "silent"
36+
};
5937

60-
after(function () {
61-
instance.cleanup();
62-
});
63-
64-
it("Watches files when multi given", function (done) {
65-
66-
assert.ok(instance.watchers.core.watchers);
67-
assert.ok(instance.watchers.core.watchers[0]);
38+
browserSync(config, function (err, bs) {
39+
assert.ok(bs.watchers.core.watchers);
40+
assert.ok(bs.watchers.core.watchers[0]);
41+
bs.cleanup();
6842
done();
6943
});
7044
});
7145

72-
describe("E2E Adding namespaced watchers", function () {
73-
74-
var instance, file;
75-
76-
before(function (done) {
77-
78-
browserSync.reset();
79-
80-
file = path.join(outpath, "watch-func.txt");
46+
it("Watches files when multi given + objs", function (done) {
47+
browserSync.reset();
8148

82-
var config = {
83-
files: [
84-
"*.html",
85-
{
86-
match: "*.css",
87-
fn: function (event, file) {
88-
console.log(file);
89-
}
49+
var config = {
50+
files: [
51+
"*.html",
52+
{
53+
match: "*.css",
54+
fn: function (event, file) {
55+
console.log(file);
9056
}
91-
],
92-
logLevel: "silent"
93-
};
94-
95-
instance = browserSync(config, done).instance;
96-
});
97-
98-
after(function () {
99-
instance.cleanup();
100-
});
101-
102-
it("Watches files when multi given + objs", function (done) {
103-
104-
assert.ok(instance.watchers.core.watchers);
105-
assert.equal(instance.watchers.core.watchers.length, 2);
57+
}
58+
],
59+
logLevel: "silent"
60+
};
61+
62+
browserSync(config, function (err, bs) {
63+
assert.ok(bs.watchers.core.watchers);
64+
assert.equal(bs.watchers.core.watchers.length, 2);
65+
bs.cleanup();
10666
done();
10767
});
10868
});

0 commit comments

Comments
 (0)