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

Commit

Permalink
fix issue #484 and #491, patch EventEmitter.once and removeAllListeners
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion committed Nov 9, 2016
1 parent 1db3876 commit da482b9
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 7 deletions.
2 changes: 0 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@
var gulp = require('gulp');
var rollup = require('gulp-rollup');
var rename = require("gulp-rename");
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var pump = require('pump');
var uglify = require('gulp-uglify');
var path = require('path');
var spawn = require('child_process').spawn;

Expand Down
49 changes: 46 additions & 3 deletions lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,16 @@ const EVENT_TASKS = zoneSymbol('eventTasks');
const ADD_EVENT_LISTENER = 'addEventListener';
const REMOVE_EVENT_LISTENER = 'removeEventListener';

interface NestedEventListener {
listener?: EventListenerOrEventListenerObject;
}

declare type NestedEventListenerOrEventListenerObject = NestedEventListener | EventListener | EventListenerObject;

interface ListenerTaskMeta extends TaskData {
useCapturing: boolean;
eventName: string;
handler: EventListenerOrEventListenerObject;
handler: NestedEventListenerOrEventListenerObject;
target: any;
name: string;
}
Expand All @@ -134,7 +139,8 @@ function findExistingRegisteredTask(
for (let i = 0; i < eventTasks.length; i++) {
const eventTask = eventTasks[i];
const data = <ListenerTaskMeta>eventTask.data;
if (data.handler === handler && data.useCapturing === capture && data.eventName === name) {
const listener = <NestedEventListener>data.handler;
if ((data.handler === handler || listener.listener === handler) && data.useCapturing === capture && data.eventName === name) {
if (remove) {
eventTasks.splice(i, 1);
}
Expand All @@ -145,6 +151,24 @@ function findExistingRegisteredTask(
return null;
}

function findAllExistingRegisteredTasks(target: any, name: string, capture: boolean, remove: boolean): Task[] {
const eventTasks: Task[] = target[EVENT_TASKS];
if (eventTasks) {
const result = [];
for (var i = eventTasks.length - 1; i >= 0; i --) {
const eventTask = eventTasks[i];
const data = <ListenerTaskMeta>eventTask.data;
if (data.eventName === name && data.useCapturing === capture) {
result.push(eventTask);
if (remove) {
eventTasks.splice(i, 1);
}
}
}
return result;
}
return null;
}

function attachRegisteredEvent(target: any, eventTask: Task): void {
let eventTasks: Task[] = target[EVENT_TASKS];
Expand Down Expand Up @@ -247,6 +271,25 @@ export function makeZoneAwareRemoveListener(fnName: string, useCapturingParam: b
};
}

export function makeZoneAwareRemoveAllListeners(fnName: string, useCapturingParam: boolean = true) {
const symbol = zoneSymbol(fnName);
const defaultUseCapturing = useCapturingParam ? false : undefined;

return function zoneAwareRemoveAllListener(self: any, args: any[]) {
var eventName = args[0];
var useCapturing = args[1] || defaultUseCapturing;
var target = self || _global;
var eventTasks = findAllExistingRegisteredTasks(target, eventName, useCapturing, true);
if (eventTasks) {
for (var i = 0; i < eventTasks.length; i ++) {
var eventTask = eventTasks[i];
eventTask.zone.cancelTask(eventTask);
}
}
target[symbol](eventName, useCapturing);
}
}

export function makeZoneAwareListeners(fnName: string) {
const symbol = zoneSymbol(fnName);

Expand Down Expand Up @@ -371,4 +414,4 @@ export function patchMethod(
proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name));
}
return delegate;
}
}
5 changes: 4 additions & 1 deletion lib/node/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {makeZoneAwareAddListener, makeZoneAwareListeners, makeZoneAwareRemoveListener, patchMethod} from '../common/utils';
import {makeZoneAwareAddListener, makeZoneAwareListeners, makeZoneAwareRemoveListener, makeZoneAwareRemoveAllListeners, patchMethod} from '../common/utils';

const callAndReturnFirstParam = (fn: (self: any, args: any[]) => any) => {
return (self: any, args: any[]) => {
Expand All @@ -19,19 +19,22 @@ const callAndReturnFirstParam = (fn: (self: any, args: any[]) => any) => {
const EE_ADD_LISTENER = 'addListener';
const EE_PREPEND_LISTENER = 'prependListener';
const EE_REMOVE_LISTENER = 'removeListener';
const EE_REMOVE_ALL_LISTENER = 'removeAllListeners';
const EE_LISTENERS = 'listeners';
const EE_ON = 'on';

const zoneAwareAddListener = callAndReturnFirstParam(makeZoneAwareAddListener(EE_ADD_LISTENER, EE_REMOVE_LISTENER, false, true));
const zoneAwarePrependListener = callAndReturnFirstParam(makeZoneAwareAddListener(EE_PREPEND_LISTENER, EE_REMOVE_LISTENER, false, true));
const zoneAwareRemoveListener = callAndReturnFirstParam(makeZoneAwareRemoveListener(EE_REMOVE_LISTENER, false));
const zoneAwareRemoveAllListeners = callAndReturnFirstParam(makeZoneAwareRemoveAllListeners(EE_REMOVE_ALL_LISTENER, false));
const zoneAwareListeners = makeZoneAwareListeners(EE_LISTENERS);

export function patchEventEmitterMethods(obj: any): boolean {
if (obj && obj.addListener) {
patchMethod(obj, EE_ADD_LISTENER, () => zoneAwareAddListener);
patchMethod(obj, EE_PREPEND_LISTENER, () => zoneAwarePrependListener);
patchMethod(obj, EE_REMOVE_LISTENER, () => zoneAwareRemoveListener);
patchMethod(obj, EE_REMOVE_ALL_LISTENER, () => zoneAwareRemoveAllListeners);
patchMethod(obj, EE_LISTENERS, () => zoneAwareListeners);
obj[EE_ON] = obj[EE_ADD_LISTENER];
return true;
Expand Down
10 changes: 9 additions & 1 deletion test/node/events.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,13 @@ describe('nodejs EventEmitter', () => {
zoneA.run(() => {
expect(emitter.listeners('test')).toEqual([]);
});
})
});
it ('should remove All listeners properly', () => {
zoneA.run(() => {
emitter.on('test', expectZoneA);
emitter.on('test', expectZoneA);
emitter.removeAllListeners('test');
expect(emitter.listeners('test').length).toEqual(0);
});
});
});

0 comments on commit da482b9

Please sign in to comment.