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

Commit 9a45b51

Browse files
committed
fix(xhr): fix #1072, should set scheduled flag to target
1 parent 1ba8519 commit 9a45b51

File tree

2 files changed

+89
-4
lines changed

2 files changed

+89
-4
lines changed

Diff for: lib/browser/browser.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
9696
const XHR_LISTENER = zoneSymbol('xhrListener');
9797
const XHR_SCHEDULED = zoneSymbol('xhrScheduled');
9898
const XHR_URL = zoneSymbol('xhrURL');
99+
const XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol('xhrErrorBeforeScheduled');
99100

100101
interface XHROptions extends TaskData {
101102
target: any;
@@ -126,9 +127,10 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
126127
const SCHEDULED = 'scheduled';
127128

128129
function scheduleTask(task: Task) {
129-
(XMLHttpRequest as any)[XHR_SCHEDULED] = false;
130130
const data = <XHROptions>task.data;
131131
const target = data.target;
132+
target[XHR_SCHEDULED] = false;
133+
target[XHR_ERROR_BEFORE_SCHEDULED] = false;
132134
// remove existing event listener
133135
const listener = target[XHR_LISTENER];
134136
if (!oriAddListener) {
@@ -143,8 +145,11 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
143145
if (target.readyState === target.DONE) {
144146
// sometimes on some browsers XMLHttpRequest will fire onreadystatechange with
145147
// readyState=4 multiple times, so we need to check task state here
146-
if (!data.aborted && (XMLHttpRequest as any)[XHR_SCHEDULED] && task.state === SCHEDULED) {
148+
if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) {
147149
task.invoke();
150+
} else if (!data.aborted && target[XHR_SCHEDULED] === false) {
151+
// error occurs when xhr.send()
152+
target[XHR_ERROR_BEFORE_SCHEDULED] = true;
148153
}
149154
}
150155
};
@@ -155,7 +160,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
155160
target[XHR_TASK] = task;
156161
}
157162
sendNative.apply(target, data.args);
158-
(XMLHttpRequest as any)[XHR_SCHEDULED] = true;
163+
target[XHR_SCHEDULED] = true;
159164
return task;
160165
}
161166

@@ -191,8 +196,15 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
191196
args: args,
192197
aborted: false
193198
};
194-
return scheduleMacroTaskWithCurrentZone(
199+
const task = scheduleMacroTaskWithCurrentZone(
195200
XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask);
201+
if (self && self[XHR_ERROR_BEFORE_SCHEDULED] === true && !options.aborted &&
202+
task.state === SCHEDULED) {
203+
// xhr request throw error when send
204+
// we should invoke task instead of leaving a scheduled
205+
// pending macroTask
206+
task.invoke();
207+
}
196208
}
197209
});
198210

Diff for: test/browser/XMLHttpRequest.spec.ts

+73
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,79 @@ describe('XMLHttpRequest', function() {
251251
});
252252
});
253253

254+
it('should trigger readystatechange if xhr request trigger cors error', (done) => {
255+
const req = new XMLHttpRequest();
256+
let err: any = null;
257+
try {
258+
req.open('get', 'file:///test', true);
259+
} catch (err) {
260+
// in IE, open will throw Access is denied error
261+
done();
262+
return;
263+
}
264+
req.addEventListener('readystatechange', function(ev) {
265+
if (req.readyState === 4) {
266+
const xhrScheduled = (req as any)['__zone_symbol__xhrScheduled'];
267+
const task = (req as any)['__zone_symbol__xhrTask'];
268+
if (xhrScheduled === false) {
269+
expect(task.state).toEqual('scheduling');
270+
setTimeout(() => {
271+
if (err) {
272+
expect(task.state).toEqual('unknown');
273+
} else {
274+
expect(task.state).toEqual('notScheduled');
275+
}
276+
done();
277+
});
278+
} else {
279+
expect(task.state).toEqual('scheduled');
280+
done();
281+
}
282+
}
283+
});
284+
try {
285+
req.send();
286+
} catch (error) {
287+
err = error;
288+
}
289+
});
290+
291+
it('should invoke task if xhr request trigger cors error', (done) => {
292+
const logs: string[] = [];
293+
const zone = Zone.current.fork({
294+
name: 'xhr',
295+
onHasTask: (delegate: ZoneDelegate, curr: Zone, target: Zone, hasTask: HasTaskState) => {
296+
logs.push(JSON.stringify(hasTask));
297+
}
298+
});
299+
const req = new XMLHttpRequest();
300+
try {
301+
req.open('get', 'file:///test', true);
302+
} catch (err) {
303+
// in IE, open will throw Access is denied error
304+
done();
305+
return;
306+
}
307+
zone.run(() => {
308+
let isError = false;
309+
let timerId = null;
310+
try {
311+
timerId = (window as any)['__zone_symbol__setTimeout'](() => {
312+
expect(logs).toEqual([
313+
`{"microTask":false,"macroTask":true,"eventTask":false,"change":"macroTask"}`,
314+
`{"microTask":false,"macroTask":false,"eventTask":false,"change":"macroTask"}`
315+
]);
316+
done();
317+
}, 500);
318+
req.send();
319+
} catch (error) {
320+
isError = true;
321+
(window as any)['__zone_symbol__clearTimeout'](timerId);
322+
done();
323+
}
324+
});
325+
});
326+
254327
it('should not throw error when get XMLHttpRequest.prototype.onreadystatechange the first time',
255328
function() {
256329
const func = function() {

0 commit comments

Comments
 (0)