mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
feat(node): add EventEmitter.errorMonitor (#3960)
This commit is contained in:
parent
92019498f6
commit
a7056095a5
2 changed files with 59 additions and 11 deletions
|
@ -32,6 +32,7 @@ export interface WrappedFunction extends Function {
|
||||||
*/
|
*/
|
||||||
export default class EventEmitter {
|
export default class EventEmitter {
|
||||||
public static defaultMaxListeners = 10;
|
public static defaultMaxListeners = 10;
|
||||||
|
public static errorMonitor = Symbol("events.errorMonitor");
|
||||||
private maxListeners: number | undefined;
|
private maxListeners: number | undefined;
|
||||||
private _events: Map<string | symbol, Array<Function | WrappedFunction>>;
|
private _events: Map<string | symbol, Array<Function | WrappedFunction>>;
|
||||||
|
|
||||||
|
@ -88,6 +89,12 @@ export default class EventEmitter {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
public emit(eventName: string | symbol, ...args: any[]): boolean {
|
public emit(eventName: string | symbol, ...args: any[]): boolean {
|
||||||
if (this._events.has(eventName)) {
|
if (this._events.has(eventName)) {
|
||||||
|
if (
|
||||||
|
eventName === "error" &&
|
||||||
|
this._events.get(EventEmitter.errorMonitor)
|
||||||
|
) {
|
||||||
|
this.emit(EventEmitter.errorMonitor, ...args);
|
||||||
|
}
|
||||||
const listeners = (this._events.get(eventName) as Function[]).slice(); // We copy with slice() so array is not mutated during emit
|
const listeners = (this._events.get(eventName) as Function[]).slice(); // We copy with slice() so array is not mutated during emit
|
||||||
for (const listener of listeners) {
|
for (const listener of listeners) {
|
||||||
try {
|
try {
|
||||||
|
@ -98,6 +105,9 @@ export default class EventEmitter {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (eventName === "error") {
|
} else if (eventName === "error") {
|
||||||
|
if (this._events.get(EventEmitter.errorMonitor)) {
|
||||||
|
this.emit(EventEmitter.errorMonitor, ...args);
|
||||||
|
}
|
||||||
const errMsg = args.length > 0 ? args[0] : Error("Unhandled error.");
|
const errMsg = args.length > 0 ? args[0] : Error("Unhandled error.");
|
||||||
throw errMsg;
|
throw errMsg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ test({
|
||||||
name:
|
name:
|
||||||
'When adding a new event, "eventListener" event is fired before adding the listener',
|
'When adding a new event, "eventListener" event is fired before adding the listener',
|
||||||
fn() {
|
fn() {
|
||||||
let eventsFired = [];
|
let eventsFired: string[] = [];
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
testEmitter.on("newListener", event => {
|
testEmitter.on("newListener", event => {
|
||||||
if (event !== "newListener") {
|
if (event !== "newListener") {
|
||||||
|
@ -36,7 +36,7 @@ test({
|
||||||
name:
|
name:
|
||||||
'When removing a listenert, "removeListener" event is fired after removal',
|
'When removing a listenert, "removeListener" event is fired after removal',
|
||||||
fn() {
|
fn() {
|
||||||
const eventsFired = [];
|
const eventsFired: string[] = [];
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
testEmitter.on("removeListener", () => {
|
testEmitter.on("removeListener", () => {
|
||||||
eventsFired.push("removeListener");
|
eventsFired.push("removeListener");
|
||||||
|
@ -80,7 +80,7 @@ test({
|
||||||
name: "Emitted events are called synchronously in the order they were added",
|
name: "Emitted events are called synchronously in the order they were added",
|
||||||
fn() {
|
fn() {
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
const eventsFired = [];
|
const eventsFired: string[] = [];
|
||||||
testEmitter.on("event", oneArg => {
|
testEmitter.on("event", oneArg => {
|
||||||
eventsFired.push("event(" + oneArg + ")");
|
eventsFired.push("event(" + oneArg + ")");
|
||||||
});
|
});
|
||||||
|
@ -162,7 +162,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: "Events can be registered to only fire once",
|
name: "Events can be registered to only fire once",
|
||||||
fn() {
|
fn() {
|
||||||
let eventsFired = [];
|
let eventsFired: string[] = [];
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
//prove multiple emits on same event first (when registered with 'on')
|
//prove multiple emits on same event first (when registered with 'on')
|
||||||
testEmitter.on("multiple event", () => {
|
testEmitter.on("multiple event", () => {
|
||||||
|
@ -187,7 +187,7 @@ test({
|
||||||
name:
|
name:
|
||||||
"You can inject a listener into the start of the stack, rather than at the end",
|
"You can inject a listener into the start of the stack, rather than at the end",
|
||||||
fn() {
|
fn() {
|
||||||
const eventsFired = [];
|
const eventsFired: string[] = [];
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
testEmitter.on("event", () => {
|
testEmitter.on("event", () => {
|
||||||
eventsFired.push("first");
|
eventsFired.push("first");
|
||||||
|
@ -206,7 +206,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: 'You can prepend a "once" listener',
|
name: 'You can prepend a "once" listener',
|
||||||
fn() {
|
fn() {
|
||||||
const eventsFired = [];
|
const eventsFired: string[] = [];
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
testEmitter.on("event", () => {
|
testEmitter.on("event", () => {
|
||||||
eventsFired.push("first");
|
eventsFired.push("first");
|
||||||
|
@ -288,7 +288,7 @@ test({
|
||||||
name: "all listeners complete execution even if removed before execution",
|
name: "all listeners complete execution even if removed before execution",
|
||||||
fn() {
|
fn() {
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
let eventsProcessed = [];
|
let eventsProcessed: string[] = [];
|
||||||
const listenerB = (): number => eventsProcessed.push("B");
|
const listenerB = (): number => eventsProcessed.push("B");
|
||||||
const listenerA = (): void => {
|
const listenerA = (): void => {
|
||||||
eventsProcessed.push("A");
|
eventsProcessed.push("A");
|
||||||
|
@ -311,7 +311,7 @@ test({
|
||||||
name: 'Raw listener will return event listener or wrapped "once" function',
|
name: 'Raw listener will return event listener or wrapped "once" function',
|
||||||
fn() {
|
fn() {
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
const eventsProcessed = [];
|
const eventsProcessed: string[] = [];
|
||||||
const listenerA = (): number => eventsProcessed.push("A");
|
const listenerA = (): number => eventsProcessed.push("A");
|
||||||
const listenerB = (): number => eventsProcessed.push("B");
|
const listenerB = (): number => eventsProcessed.push("B");
|
||||||
testEmitter.on("event", listenerA);
|
testEmitter.on("event", listenerA);
|
||||||
|
@ -335,7 +335,7 @@ test({
|
||||||
"Once wrapped raw listeners may be executed multiple times, until the wrapper is executed",
|
"Once wrapped raw listeners may be executed multiple times, until the wrapper is executed",
|
||||||
fn() {
|
fn() {
|
||||||
const testEmitter = new EventEmitter();
|
const testEmitter = new EventEmitter();
|
||||||
let eventsProcessed = [];
|
let eventsProcessed: string[] = [];
|
||||||
const listenerA = (): number => eventsProcessed.push("A");
|
const listenerA = (): number => eventsProcessed.push("A");
|
||||||
testEmitter.once("once-event", listenerA);
|
testEmitter.once("once-event", listenerA);
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: "Can add once event listener to EventEmitter via standalone function",
|
name: "Can add once event listener to EventEmitter via standalone function",
|
||||||
async fn() {
|
async fn() {
|
||||||
const ee: EventEmitter = new EventEmitter();
|
const ee = new EventEmitter();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ee.emit("event", 42, "foo");
|
ee.emit("event", 42, "foo");
|
||||||
}, 0);
|
}, 0);
|
||||||
|
@ -383,7 +383,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: "Only valid integers are allowed for max listeners",
|
name: "Only valid integers are allowed for max listeners",
|
||||||
fn() {
|
fn() {
|
||||||
const ee: EventEmitter = new EventEmitter();
|
const ee = new EventEmitter();
|
||||||
ee.setMaxListeners(0);
|
ee.setMaxListeners(0);
|
||||||
assertThrows(
|
assertThrows(
|
||||||
() => {
|
() => {
|
||||||
|
@ -401,3 +401,41 @@ test({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "ErrorMonitor can spy on error events without consuming them",
|
||||||
|
fn() {
|
||||||
|
const ee = new EventEmitter();
|
||||||
|
let events: string[] = [];
|
||||||
|
//unhandled error scenario should throw
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
ee.emit("error");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"Unhandled error"
|
||||||
|
);
|
||||||
|
|
||||||
|
ee.on(EventEmitter.errorMonitor, () => {
|
||||||
|
events.push("errorMonitor event");
|
||||||
|
});
|
||||||
|
|
||||||
|
//error is still unhandled but also intercepted by error monitor
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
ee.emit("error");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"Unhandled error"
|
||||||
|
);
|
||||||
|
assertEquals(events, ["errorMonitor event"]);
|
||||||
|
|
||||||
|
//A registered error handler won't throw, but still be monitored
|
||||||
|
events = [];
|
||||||
|
ee.on("error", () => {
|
||||||
|
events.push("error");
|
||||||
|
});
|
||||||
|
ee.emit("error");
|
||||||
|
assertEquals(events, ["errorMonitor event", "error"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue