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

Commit a4ac8d2

Browse files
committed
fix(event): fix #836, handle event callback call removeEventListener case
1 parent 3a4bfbd commit a4ac8d2

File tree

2 files changed

+415
-6
lines changed

2 files changed

+415
-6
lines changed

Diff for: lib/common/events.ts

+30-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export interface PatchEventTargetOptions {
4545
export function patchEventTarget(
4646
api: _ZonePrivate, _global: any, apis: any[], patchOptions?: PatchEventTargetOptions) {
4747
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+
}
4853
const delegate = task.callback;
4954
if (typeof delegate === OBJECT_TYPE && delegate.handleEvent) {
5055
// create the bind version of handleEvnet when invoke
@@ -62,8 +67,16 @@ export function patchEventTarget(
6267
const tasks = target[zoneSymbolEventNames[event.type][FALSE_STR]];
6368
if (tasks) {
6469
// 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+
}
6780
}
6881
}
6982
};
@@ -74,8 +87,17 @@ export function patchEventTarget(
7487

7588
const tasks = target[zoneSymbolEventNames[event.type][TRUE_STR]];
7689
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+
}
79101
}
80102
}
81103
};
@@ -169,7 +191,7 @@ export function patchEventTarget(
169191
// if all tasks for the eventName + capture have gone,
170192
// we will really remove the global event callback,
171193
// if not, return
172-
if (!task.remove) {
194+
if (!task.allRemoved) {
173195
return;
174196
}
175197
return nativeRemoveEventListener.apply(task.target, [
@@ -372,10 +394,12 @@ export function patchEventTarget(
372394
const typeOfDelegate = typeof delegate;
373395
if (compare(existingTask, delegate)) {
374396
existingTasks.splice(i, 1);
397+
// set isRemoved to data for faster invokeTask check
398+
(existingTask as any).isRemoved = true;
375399
if (existingTasks.length === 0) {
376400
// all tasks for the eventName + capture have gone,
377401
// remove globalZoneAwareCallback and remove the task cache from target
378-
(existingTask as any).remove = true;
402+
(existingTask as any).allRemoved = true;
379403
target[symbolEventName] = null;
380404
}
381405
existingTask.zone.cancelTask(existingTask);

0 commit comments

Comments
 (0)