@@ -45,6 +45,11 @@ export interface PatchEventTargetOptions {
45
45
export function patchEventTarget (
46
46
api : _ZonePrivate , _global : any , apis : any [ ] , patchOptions ?: PatchEventTargetOptions ) {
47
47
const invokeTask = function ( task : any , target : any , event : Event ) {
48
+ // for better performance, check isRemoved which is set
49
+ // by removeEventListener
50
+ if ( task . isRemoved ) {
51
+ return ;
52
+ }
48
53
const delegate = task . callback ;
49
54
if ( typeof delegate === OBJECT_TYPE && delegate . handleEvent ) {
50
55
// create the bind version of handleEvnet when invoke
@@ -62,8 +67,16 @@ export function patchEventTarget(
62
67
const tasks = target [ zoneSymbolEventNames [ event . type ] [ FALSE_STR ] ] ;
63
68
if ( tasks ) {
64
69
// invoke all tasks which attached to current target with given event.type and capture = false
65
- for ( let i = 0 ; i < tasks . length ; i ++ ) {
66
- invokeTask ( tasks [ i ] , target , event ) ;
70
+ if ( tasks . length === 1 ) {
71
+ invokeTask ( tasks [ 0 ] , target , event ) ;
72
+ } else {
73
+ // https://github.com/angular/zone.js/issues/836
74
+ // copy the tasks array before invoke, to avoid
75
+ // the callback will remove itself or other listener
76
+ const copyTasks = tasks . slice ( ) ;
77
+ for ( let i = 0 ; i < copyTasks . length ; i ++ ) {
78
+ invokeTask ( copyTasks [ i ] , target , event ) ;
79
+ }
67
80
}
68
81
}
69
82
} ;
@@ -74,8 +87,17 @@ export function patchEventTarget(
74
87
75
88
const tasks = target [ zoneSymbolEventNames [ event . type ] [ TRUE_STR ] ] ;
76
89
if ( tasks ) {
77
- for ( let i = 0 ; i < tasks . length ; i ++ ) {
78
- invokeTask ( tasks [ i ] , target , event ) ;
90
+ // invoke all tasks which attached to current target with given event.type and capture = false
91
+ if ( tasks . length === 1 ) {
92
+ invokeTask ( tasks [ 0 ] , target , event ) ;
93
+ } else {
94
+ // https://github.com/angular/zone.js/issues/836
95
+ // copy the tasks array before invoke, to avoid
96
+ // the callback will remove itself or other listener
97
+ const copyTasks = tasks . slice ( ) ;
98
+ for ( let i = 0 ; i < copyTasks . length ; i ++ ) {
99
+ invokeTask ( copyTasks [ i ] , target , event ) ;
100
+ }
79
101
}
80
102
}
81
103
} ;
@@ -169,7 +191,7 @@ export function patchEventTarget(
169
191
// if all tasks for the eventName + capture have gone,
170
192
// we will really remove the global event callback,
171
193
// if not, return
172
- if ( ! task . remove ) {
194
+ if ( ! task . allRemoved ) {
173
195
return ;
174
196
}
175
197
return nativeRemoveEventListener . apply ( task . target , [
@@ -372,10 +394,12 @@ export function patchEventTarget(
372
394
const typeOfDelegate = typeof delegate ;
373
395
if ( compare ( existingTask , delegate ) ) {
374
396
existingTasks . splice ( i , 1 ) ;
397
+ // set isRemoved to data for faster invokeTask check
398
+ ( existingTask as any ) . isRemoved = true ;
375
399
if ( existingTasks . length === 0 ) {
376
400
// all tasks for the eventName + capture have gone,
377
401
// remove globalZoneAwareCallback and remove the task cache from target
378
- ( existingTask as any ) . remove = true ;
402
+ ( existingTask as any ) . allRemoved = true ;
379
403
target [ symbolEventName ] = null ;
380
404
}
381
405
existingTask . zone . cancelTask ( existingTask ) ;
0 commit comments