fix(zone.js): should invoke xhr send task when no response error occurs (#38836)

Close #38795

in the XMLHttpRequest patch, when get `readystatechange` event, zone.js try to
invoke `load` event listener first, then call `invokeTask` to finish the
`XMLHttpRequest::send` macroTask, but if the request failed because the
server can not be reached, the `load` event listener will not be invoked,
so the `invokeTask` of the `XMLHttpRequest::send` will not be triggered either,
so we will have a non finished macroTask there which will make the Zone
not stable, also memory leak.

So in this PR, if the `XMLHttpRequest.status = 0` when we get the `readystatechange`
event, that means something wents wrong before we reached the server, we need to
invoke the task to finish the macroTask.

PR Close #38836
This commit is contained in:
JiaLiPassion 2020-09-14 15:54:12 +09:00 committed by Misko Hevery
parent 55485713a3
commit d92a0dd72f
2 changed files with 29 additions and 1 deletions

View File

@ -149,8 +149,12 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
// check whether the xhr has registered onload listener
// if that is the case, the task should invoke after all
// onload listeners finish.
// Also if the request failed without response (status = 0), the load event handler
// will not be triggered, in that case, we should also invoke the placeholder callback
// to close the XMLHttpRequest::send macroTask.
// https://github.com/angular/angular/issues/38795
const loadTasks = target[Zone.__symbol__('loadfalse')];
if (loadTasks && loadTasks.length > 0) {
if (target.status !== 0 && loadTasks && loadTasks.length > 0) {
const oriInvoke = task.invoke;
task.invoke = function() {
// need to load the tasks again, because in other

View File

@ -268,6 +268,30 @@ describe('XMLHttpRequest', function() {
});
});
it('should close xhr request if error happened when connect', function(done) {
const logs: boolean[] = [];
Zone.current
.fork({
name: 'xhr',
onHasTask:
(delegate: ZoneDelegate, curr: Zone, target: Zone, taskState: HasTaskState) => {
if (taskState.change === 'macroTask') {
logs.push(taskState.macroTask);
}
return delegate.hasTask(target, taskState);
}
})
.run(function() {
const req = new XMLHttpRequest();
req.open('get', 'http://notexists.url', true);
req.send();
req.addEventListener('error', () => {
expect(logs).toEqual([true, false]);
done();
});
});
});
it('should trigger readystatechange if xhr request trigger cors error', (done) => {
const req = new XMLHttpRequest();
let err: any = null;