diff --git a/lib/zone.ts b/lib/zone.ts index bf4f49819..374e7941e 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -756,10 +756,22 @@ const Zone: ZoneType = (function(global: any) { runTask(task: Task, applyThis?: any, applyArgs?: any): any { - if (task.zone != this) + if (task.zone != this) { throw new Error( 'A task can only be run in the zone of creation! (Creation: ' + (task.zone || NO_ZONE).name + '; Execution: ' + this.name + ')'); + } + // https://github.com/angular/zone.js/issues/778, sometimes eventTask + // will run in notScheduled(canceled) state, we should not try to + // run such kind of task but just return + + // we have to define an variable here, if not + // typescript compiler will complain below + const isNotScheduled = task.state === notScheduled; + if (isNotScheduled && task.type === eventTask) { + return; + } + const reEntryGuard = task.state != running; reEntryGuard && (task as ZoneTask)._transitionTo(running, scheduled); task.runCount++; diff --git a/test/common/task.spec.ts b/test/common/task.spec.ts index 44bcb9eb5..da1286244 100644 --- a/test/common/task.spec.ts +++ b/test/common/task.spec.ts @@ -873,6 +873,26 @@ describe('task lifecycle', () => { {toState: 'notScheduled', fromState: 'running'} ]); })); + + it('task should not run if task transite to notScheduled state which was canceled', + testFnWithLoggedTransitionTo(() => { + let task: Task; + Zone.current.fork({name: 'testCancelZone'}).run(() => { + const task = Zone.current.scheduleEventTask('testEventTask', noop, null, noop, noop); + Zone.current.cancelTask(task); + task.invoke(); + }); + expect(log.map(item => { + return {toState: item.toState, fromState: item.fromState}; + })) + .toEqual([ + {toState: 'scheduling', fromState: 'notScheduled'}, + {toState: 'scheduled', fromState: 'scheduling'}, + {toState: 'canceling', fromState: 'scheduled'}, + {toState: 'notScheduled', fromState: 'canceling'} + ]); + })); + }); describe('reschedule zone', () => {