Skip to content

Commit 54e7576

Browse files
lymlalexeagle
authored andcommitted
fix(core): fix chained http call (#20924)
Fixes an issue where chained http calls would prematurely call testability whenStable callbacks after the first http call. Fixes #20921 PR Close #20924
1 parent d3333f0 commit 54e7576

File tree

2 files changed

+44
-37
lines changed

2 files changed

+44
-37
lines changed

packages/core/src/testability/testability.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,22 @@ export class Testability implements PublicTestability {
9898
/** @internal */
9999
_runCallbacksIfReady(): void {
100100
if (this.isStable()) {
101-
// Schedules the call backs in a new frame so that it is always async.
102-
scheduleMicroTask(() => {
103-
while (this._callbacks.length !== 0) {
104-
(this._callbacks.pop() !)(this._didWork);
105-
}
101+
if (this._callbacks.length !== 0) {
102+
// Schedules the call backs after a macro task run outside of the angular zone to make sure
103+
// no new task are added
104+
this._ngZone.runOutsideAngular(() => {
105+
setTimeout(() => {
106+
if (this.isStable()) {
107+
while (this._callbacks.length !== 0) {
108+
(this._callbacks.pop() !)(this._didWork);
109+
}
110+
this._didWork = false;
111+
}
112+
});
113+
});
114+
} else {
106115
this._didWork = false;
107-
});
116+
}
108117
} else {
109118
// Not Ready
110119
this._didWork = true;

packages/core/test/testability/testability_spec.ts

+29-31
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@ import {scheduleMicroTask} from '../../src/util';
1616

1717

1818

19-
// Schedules a microtasks (using a resolved promise .then())
20-
function microTask(fn: Function): void {
21-
scheduleMicroTask(() => {
22-
// We do double dispatch so that we can wait for scheduleMicrotask in the Testability when
23-
// NgZone becomes stable.
24-
scheduleMicroTask(fn);
25-
});
19+
// Schedules a task to be run after Testability checks for oustanding tasks. Since Testability
20+
// uses a 0 second timeout to check for outstanding tasks we add our 0 second timeout after a
21+
// micro task (which ensures Testability's timeout is run first).
22+
function afterTestabilityCheck(fn: Function): void {
23+
scheduleMicroTask(() => setTimeout(fn));
2624
}
2725

2826
@Injectable()
@@ -65,7 +63,7 @@ class MockNgZone extends NgZone {
6563
it('should fire whenstable callbacks if pending count is 0',
6664
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
6765
testability.whenStable(execute);
68-
microTask(() => {
66+
afterTestabilityCheck(() => {
6967
expect(execute).toHaveBeenCalled();
7068
async.done();
7169
});
@@ -82,11 +80,11 @@ class MockNgZone extends NgZone {
8280
testability.increasePendingRequestCount();
8381
testability.whenStable(execute);
8482

85-
microTask(() => {
83+
afterTestabilityCheck(() => {
8684
expect(execute).not.toHaveBeenCalled();
8785
testability.decreasePendingRequestCount();
8886

89-
microTask(() => {
87+
afterTestabilityCheck(() => {
9088
expect(execute).not.toHaveBeenCalled();
9189
async.done();
9290
});
@@ -98,11 +96,11 @@ class MockNgZone extends NgZone {
9896
testability.increasePendingRequestCount();
9997
testability.whenStable(execute);
10098

101-
microTask(() => {
99+
afterTestabilityCheck(() => {
102100
expect(execute).not.toHaveBeenCalled();
103101
testability.decreasePendingRequestCount();
104102

105-
microTask(() => {
103+
afterTestabilityCheck(() => {
106104
expect(execute).toHaveBeenCalled();
107105
async.done();
108106
});
@@ -120,7 +118,7 @@ class MockNgZone extends NgZone {
120118
it('should fire whenstable callbacks with didWork if pending count is 0',
121119
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
122120
testability.whenStable(execute);
123-
microTask(() => {
121+
afterTestabilityCheck(() => {
124122
expect(execute).toHaveBeenCalledWith(false);
125123
async.done();
126124
});
@@ -131,14 +129,14 @@ class MockNgZone extends NgZone {
131129
testability.increasePendingRequestCount();
132130
testability.whenStable(execute);
133131

134-
microTask(() => {
132+
afterTestabilityCheck(() => {
135133
testability.decreasePendingRequestCount();
136134

137-
microTask(() => {
135+
afterTestabilityCheck(() => {
138136
expect(execute).toHaveBeenCalledWith(true);
139137
testability.whenStable(execute2);
140138

141-
microTask(() => {
139+
afterTestabilityCheck(() => {
142140
expect(execute2).toHaveBeenCalledWith(false);
143141
async.done();
144142
});
@@ -154,7 +152,7 @@ class MockNgZone extends NgZone {
154152
ngZone.stable();
155153
testability.whenStable(execute);
156154

157-
microTask(() => {
155+
afterTestabilityCheck(() => {
158156
expect(execute).toHaveBeenCalled();
159157
async.done();
160158
});
@@ -173,11 +171,11 @@ class MockNgZone extends NgZone {
173171
ngZone.unstable();
174172
testability.whenStable(execute);
175173

176-
microTask(() => {
174+
afterTestabilityCheck(() => {
177175
expect(execute).not.toHaveBeenCalled();
178176
ngZone.stable();
179177

180-
microTask(() => {
178+
afterTestabilityCheck(() => {
181179
expect(execute).toHaveBeenCalled();
182180
async.done();
183181
});
@@ -198,15 +196,15 @@ class MockNgZone extends NgZone {
198196
testability.increasePendingRequestCount();
199197
testability.whenStable(execute);
200198

201-
microTask(() => {
199+
afterTestabilityCheck(() => {
202200
expect(execute).not.toHaveBeenCalled();
203201
testability.decreasePendingRequestCount();
204202

205-
microTask(() => {
203+
afterTestabilityCheck(() => {
206204
expect(execute).not.toHaveBeenCalled();
207205
ngZone.stable();
208206

209-
microTask(() => {
207+
afterTestabilityCheck(() => {
210208
expect(execute).toHaveBeenCalled();
211209
async.done();
212210
});
@@ -221,19 +219,19 @@ class MockNgZone extends NgZone {
221219
testability.increasePendingRequestCount();
222220
testability.whenStable(execute);
223221

224-
microTask(() => {
222+
afterTestabilityCheck(() => {
225223
expect(execute).not.toHaveBeenCalled();
226224
ngZone.stable();
227225

228-
microTask(() => {
226+
afterTestabilityCheck(() => {
229227
expect(execute).not.toHaveBeenCalled();
230228
testability.decreasePendingRequestCount();
231229

232-
microTask(() => {
230+
afterTestabilityCheck(() => {
233231
expect(execute).not.toHaveBeenCalled();
234232
testability.decreasePendingRequestCount();
235233

236-
microTask(() => {
234+
afterTestabilityCheck(() => {
237235
expect(execute).toHaveBeenCalled();
238236
async.done();
239237
});
@@ -248,11 +246,11 @@ class MockNgZone extends NgZone {
248246
ngZone.stable();
249247
testability.whenStable(execute);
250248

251-
microTask(() => {
249+
afterTestabilityCheck(() => {
252250
expect(execute).toHaveBeenCalledWith(true);
253251
testability.whenStable(execute2);
254252

255-
microTask(() => {
253+
afterTestabilityCheck(() => {
256254
expect(execute2).toHaveBeenCalledWith(false);
257255
async.done();
258256
});
@@ -264,14 +262,14 @@ class MockNgZone extends NgZone {
264262
ngZone.unstable();
265263
testability.whenStable(execute);
266264

267-
microTask(() => {
265+
afterTestabilityCheck(() => {
268266
ngZone.stable();
269267

270-
microTask(() => {
268+
afterTestabilityCheck(() => {
271269
expect(execute).toHaveBeenCalledWith(true);
272270
testability.whenStable(execute2);
273271

274-
microTask(() => {
272+
afterTestabilityCheck(() => {
275273
expect(execute2).toHaveBeenCalledWith(false);
276274
async.done();
277275
});

0 commit comments

Comments
 (0)