Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit ba72f34

Browse files
committed
feat: expose hooks for enqueuing and dequing tasks
1 parent 21b47ae commit ba72f34

File tree

3 files changed

+185
-34
lines changed

3 files changed

+185
-34
lines changed

counting-zone.js

+25-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
/*
22
* See example/counting.html
33
*/
4+
45
Zone.countingZone = {
5-
'-onZoneCreated': function () {
6-
Zone.countingZone.counter += 1;
6+
7+
// setTimeout
8+
enqueueTask: function () {
9+
this.data.count += 1;
710
},
8-
'+afterTask': function () {
9-
Zone.countingZone.counter -= 1;
10-
if (Zone.countingZone.counter <= 0) {
11-
Zone.countingZone.counter = 0;
12-
this.onFlush();
13-
}
11+
12+
// fires when...
13+
// - clearTimeout
14+
// - setTimeout finishes
15+
dequeueTask: function () {
16+
this.data.count -= 1;
1417
},
15-
'-run': function () {
16-
Zone.countingZone.counter = 0;
18+
19+
afterTask: function () {
20+
if (this.data.count === 0 && !this.data.flushed) {
21+
this.data.flushed = true;
22+
this.run(this.onFlush);
23+
}
1724
},
25+
1826
counter: function () {
19-
return Zone.countingZone.counter;
27+
return this.data.count;
28+
},
29+
30+
data: {
31+
count: 0,
32+
flushed: false
2033
},
34+
2135
onFlush: function () {}
2236
};

test/counting-zone.spec.js

+70-13
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,90 @@
11
'use strict';
22

33
describe('Zone.countingZone', function () {
4-
var flushSpy = jasmine.createSpy('flush'),
5-
countingZone = zone.fork(Zone.countingZone).fork({
6-
onFlush: flushSpy
7-
});
4+
var flushSpy, countingZone;
85

96
beforeEach(function () {
10-
jasmine.Clock.useMock();
11-
flushSpy.reset();
7+
flushSpy = jasmine.createSpy('flush');
8+
countingZone = zone.fork(Zone.longStackTraceZone).
9+
fork(Zone.countingZone).
10+
fork({
11+
onFlush: flushSpy
12+
});
1213
});
1314

1415
it('should flush at the end of a run', function () {
15-
countingZone.run(function () {});
16-
expect(flushSpy).toHaveBeenCalled();
16+
countingZone.run(function () {
17+
expect(countingZone.counter()).toBe(0);
18+
});
1719
expect(countingZone.counter()).toBe(0);
20+
expect(flushSpy.calls.length).toBe(1);
21+
});
22+
23+
it('should work with setTimeout', function () {
24+
var latch;
25+
26+
runs(function () {
27+
countingZone.run(function () {
28+
setTimeout(function () {
29+
latch = true;
30+
}, 0);
31+
expect(countingZone.counter()).toBe(1);
32+
});
33+
});
34+
35+
waitsFor(function () {
36+
return latch;
37+
});
38+
39+
runs(function () {
40+
expect(countingZone.counter()).toBe(0);
41+
})
1842
});
1943

20-
it('should work', function () {
44+
it('should work with clearTimeout', function () {
45+
var latch = false;
2146
countingZone.run(function () {
47+
var id = setTimeout(function () {
48+
latch = true;
49+
}, 0);
50+
expect(countingZone.counter()).toBe(1);
51+
clearTimeout(id);
52+
expect(countingZone.counter()).toBe(0);
53+
});
54+
});
55+
2256

23-
setTimeout(function () {}, 0);
57+
it('should work with addEventListener', function () {
58+
var elt = document.createElement('button');
59+
var clicked = false;
60+
61+
runs(function () {
62+
countingZone.run(main);
63+
});
64+
65+
function main () {
66+
expect(countingZone.counter()).toBe(0);
67+
elt.addEventListener('click', onClick);
2468
expect(countingZone.counter()).toBe(1);
2569

26-
//jasmine.Clock.tick(0);
70+
elt.click();
71+
function onClick () {
72+
expect(countingZone.counter()).toBe(1);
73+
elt.removeEventListener('click', onClick);
74+
expect(countingZone.counter()).toBe(0);
75+
clicked = true;
76+
}
77+
78+
expect(countingZone.counter()).toBe(0);
79+
}
80+
81+
waitsFor(function () {
82+
return clicked;
83+
}, 10, 'the thing');
2784

28-
//expect(countingZone.counter()).toBe(0);
85+
runs(function () {
86+
expect(flushSpy.calls.length).toBe(1);
2987
});
3088

31-
//jasmine.Clock.tick(0);
3289
});
3390
});

zone.js

+90-10
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,21 @@ Zone.prototype = {
5858
},
5959

6060
bind: function (fn) {
61+
this.enqueueTask(fn);
6162
var zone = this.fork();
6263
return function zoneBoundFn() {
6364
return zone.run(fn, this, arguments);
6465
};
6566
},
6667

68+
bindOnce: function (fn) {
69+
return this.bind(function () {
70+
var result = fn.apply(this, arguments);
71+
zone.dequeueTask(fn);
72+
return result;
73+
});
74+
},
75+
6776
run: function run (fn, applyTo, applyWith) {
6877
applyWith = applyWith || [];
6978

@@ -90,18 +99,72 @@ Zone.prototype = {
9099

91100
beforeTask: function () {},
92101
onZoneCreated: function () {},
93-
afterTask: function () {}
102+
afterTask: function () {},
103+
enqueueTask: function () {},
104+
dequeueTask: function () {}
94105
};
95106

96-
Zone.patchFn = function (obj, fnNames) {
107+
108+
Zone.patchSetClearFn = function (obj, fnNames) {
109+
fnNames.map(function (name) {
110+
return name[0].toUpperCase() + name.substr(1);
111+
}).
112+
forEach(function (name) {
113+
var setName = 'set' + name;
114+
var clearName = 'clear' + name;
115+
var delegate = obj[setName];
116+
117+
if (delegate) {
118+
var ids = {};
119+
120+
zone[setName] = function (fn) {
121+
var id;
122+
arguments[0] = function () {
123+
delete ids[id];
124+
return fn.apply(this, arguments);
125+
};
126+
var args = Zone.bindArgumentsOnce(arguments);
127+
id = delegate.apply(obj, args);
128+
ids[id] = true;
129+
return id;
130+
};
131+
132+
obj[setName] = function () {
133+
return zone[setName].apply(this, arguments);
134+
};
135+
136+
var clearDelegate = obj[clearName];
137+
138+
zone[clearName] = function (id) {
139+
if (ids[id]) {
140+
delete ids[id];
141+
zone.dequeueTask();
142+
}
143+
return clearDelegate.apply(this, arguments);
144+
};
145+
146+
obj[clearName] = function () {
147+
return zone[clearName].apply(this, arguments);
148+
};
149+
}
150+
});
151+
};
152+
153+
154+
Zone.patchSetFn = function (obj, fnNames) {
97155
fnNames.forEach(function (name) {
98156
var delegate = obj[name];
157+
99158
if (delegate) {
100-
zone[name] = function () {
101-
return delegate.apply(obj, Zone.bindArguments(arguments));
159+
zone[name] = function (fn) {
160+
arguments[0] = function () {
161+
return fn.apply(this, arguments);
162+
};
163+
var args = Zone.bindArgumentsOnce(arguments);
164+
return delegate.apply(obj, args);
102165
};
103166

104-
obj[name] = function marker () {
167+
obj[name] = function () {
105168
return zone[name].apply(this, arguments);
106169
};
107170
}
@@ -128,6 +191,16 @@ Zone.bindArguments = function (args) {
128191
return args;
129192
};
130193

194+
195+
Zone.bindArgumentsOnce = function (args) {
196+
for (var i = args.length - 1; i >= 0; i--) {
197+
if (typeof args[i] === 'function') {
198+
args[i] = zone.bindOnce(args[i]);
199+
}
200+
}
201+
return args;
202+
};
203+
131204
Zone.patchableFn = function (obj, fnNames) {
132205
fnNames.forEach(function (name) {
133206
var delegate = obj[name];
@@ -206,17 +279,24 @@ Zone.patchEventTargetMethods = function (obj) {
206279
var removeDelegate = obj.removeEventListener;
207280
obj.removeEventListener = function (eventName, fn) {
208281
arguments[1] = arguments[1]._bound || arguments[1];
209-
return removeDelegate.apply(this, arguments);
282+
var result = removeDelegate.apply(this, arguments);
283+
zone.dequeueTask(fn);
284+
return result;
210285
};
211286
};
212287

213288
Zone.patch = function patch () {
214-
Zone.patchFn(window, [
215-
'setTimeout',
216-
'setInterval',
289+
Zone.patchSetClearFn(window, [
290+
'timeout',
291+
'interval',
292+
'immediate'
293+
]);
294+
295+
Zone.patchSetFn(window, [
217296
'requestAnimationFrame',
218297
'webkitRequestAnimationFrame'
219298
]);
299+
220300
Zone.patchableFn(window, ['alert', 'prompt']);
221301

222302
// patched properties depend on addEventListener, so this needs to come first
@@ -316,7 +396,7 @@ Zone.patchViaCapturingAllTheEvents = function () {
316396
});
317397
};
318398

319-
// TODO: wrap some native API
399+
// wrap some native API on `window`
320400
Zone.patchClass = function (className) {
321401
var OriginalClass = window[className];
322402
if (!OriginalClass) {

0 commit comments

Comments
 (0)