mirror of
https://github.com/nodejs/node.git
synced 2025-05-05 10:27:05 +00:00

Allows an AbortSignal to be passed in to events.once() to cancel waiting on an event. Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/34911 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
213 lines
4.9 KiB
JavaScript
213 lines
4.9 KiB
JavaScript
'use strict';
|
|
// Flags: --expose-internals --no-warnings
|
|
|
|
const common = require('../common');
|
|
const { once, EventEmitter } = require('events');
|
|
const {
|
|
strictEqual,
|
|
deepStrictEqual,
|
|
fail,
|
|
rejects,
|
|
} = require('assert');
|
|
const { EventTarget, Event } = require('internal/event_target');
|
|
|
|
async function onceAnEvent() {
|
|
const ee = new EventEmitter();
|
|
|
|
process.nextTick(() => {
|
|
ee.emit('myevent', 42);
|
|
});
|
|
|
|
const [value] = await once(ee, 'myevent');
|
|
strictEqual(value, 42);
|
|
strictEqual(ee.listenerCount('error'), 0);
|
|
strictEqual(ee.listenerCount('myevent'), 0);
|
|
}
|
|
|
|
async function onceAnEventWithTwoArgs() {
|
|
const ee = new EventEmitter();
|
|
|
|
process.nextTick(() => {
|
|
ee.emit('myevent', 42, 24);
|
|
});
|
|
|
|
const value = await once(ee, 'myevent');
|
|
deepStrictEqual(value, [42, 24]);
|
|
}
|
|
|
|
async function catchesErrors() {
|
|
const ee = new EventEmitter();
|
|
|
|
const expected = new Error('kaboom');
|
|
let err;
|
|
process.nextTick(() => {
|
|
ee.emit('error', expected);
|
|
});
|
|
|
|
try {
|
|
await once(ee, 'myevent');
|
|
} catch (_e) {
|
|
err = _e;
|
|
}
|
|
strictEqual(err, expected);
|
|
strictEqual(ee.listenerCount('error'), 0);
|
|
strictEqual(ee.listenerCount('myevent'), 0);
|
|
}
|
|
|
|
async function stopListeningAfterCatchingError() {
|
|
const ee = new EventEmitter();
|
|
|
|
const expected = new Error('kaboom');
|
|
let err;
|
|
process.nextTick(() => {
|
|
ee.emit('error', expected);
|
|
ee.emit('myevent', 42, 24);
|
|
});
|
|
|
|
try {
|
|
await once(ee, 'myevent');
|
|
} catch (_e) {
|
|
err = _e;
|
|
}
|
|
process.removeAllListeners('multipleResolves');
|
|
strictEqual(err, expected);
|
|
strictEqual(ee.listenerCount('error'), 0);
|
|
strictEqual(ee.listenerCount('myevent'), 0);
|
|
}
|
|
|
|
async function onceError() {
|
|
const ee = new EventEmitter();
|
|
|
|
const expected = new Error('kaboom');
|
|
process.nextTick(() => {
|
|
ee.emit('error', expected);
|
|
});
|
|
|
|
const promise = once(ee, 'error');
|
|
strictEqual(ee.listenerCount('error'), 1);
|
|
const [ err ] = await promise;
|
|
strictEqual(err, expected);
|
|
strictEqual(ee.listenerCount('error'), 0);
|
|
strictEqual(ee.listenerCount('myevent'), 0);
|
|
}
|
|
|
|
async function onceWithEventTarget() {
|
|
const et = new EventTarget();
|
|
const event = new Event('myevent');
|
|
process.nextTick(() => {
|
|
et.dispatchEvent(event);
|
|
});
|
|
const [ value ] = await once(et, 'myevent');
|
|
strictEqual(value, event);
|
|
}
|
|
|
|
async function onceWithEventTargetError() {
|
|
const et = new EventTarget();
|
|
const error = new Event('error');
|
|
process.nextTick(() => {
|
|
et.dispatchEvent(error);
|
|
});
|
|
|
|
const [ err ] = await once(et, 'error');
|
|
strictEqual(err, error);
|
|
}
|
|
|
|
async function prioritizesEventEmitter() {
|
|
const ee = new EventEmitter();
|
|
ee.addEventListener = fail;
|
|
ee.removeAllListeners = fail;
|
|
process.nextTick(() => ee.emit('foo'));
|
|
await once(ee, 'foo');
|
|
}
|
|
|
|
async function abortSignalBefore() {
|
|
const ee = new EventEmitter();
|
|
const ac = new AbortController();
|
|
ee.on('error', common.mustNotCall());
|
|
ac.abort();
|
|
|
|
await Promise.all([1, {}, 'hi', null, false].map((signal) => {
|
|
return rejects(once(ee, 'foo', { signal }), {
|
|
code: 'ERR_INVALID_ARG_TYPE'
|
|
});
|
|
}));
|
|
|
|
return rejects(once(ee, 'foo', { signal: ac.signal }), {
|
|
name: 'AbortError'
|
|
});
|
|
}
|
|
|
|
async function abortSignalAfter() {
|
|
const ee = new EventEmitter();
|
|
const ac = new AbortController();
|
|
ee.on('error', common.mustNotCall());
|
|
const r = rejects(once(ee, 'foo', { signal: ac.signal }), {
|
|
name: 'AbortError'
|
|
});
|
|
process.nextTick(() => ac.abort());
|
|
return r;
|
|
}
|
|
|
|
async function abortSignalAfterEvent() {
|
|
const ee = new EventEmitter();
|
|
const ac = new AbortController();
|
|
process.nextTick(() => {
|
|
ee.emit('foo');
|
|
ac.abort();
|
|
});
|
|
await once(ee, 'foo', { signal: ac.signal });
|
|
}
|
|
|
|
async function eventTargetAbortSignalBefore() {
|
|
const et = new EventTarget();
|
|
const ac = new AbortController();
|
|
ac.abort();
|
|
|
|
await Promise.all([1, {}, 'hi', null, false].map((signal) => {
|
|
return rejects(once(et, 'foo', { signal }), {
|
|
code: 'ERR_INVALID_ARG_TYPE'
|
|
});
|
|
}));
|
|
|
|
return rejects(once(et, 'foo', { signal: ac.signal }), {
|
|
name: 'AbortError'
|
|
});
|
|
}
|
|
|
|
async function eventTargetAbortSignalAfter() {
|
|
const et = new EventTarget();
|
|
const ac = new AbortController();
|
|
const r = rejects(once(et, 'foo', { signal: ac.signal }), {
|
|
name: 'AbortError'
|
|
});
|
|
process.nextTick(() => ac.abort());
|
|
return r;
|
|
}
|
|
|
|
async function eventTargetAbortSignalAfterEvent() {
|
|
const et = new EventTarget();
|
|
const ac = new AbortController();
|
|
process.nextTick(() => {
|
|
et.dispatchEvent(new Event('foo'));
|
|
ac.abort();
|
|
});
|
|
await once(et, 'foo', { signal: ac.signal });
|
|
}
|
|
|
|
Promise.all([
|
|
onceAnEvent(),
|
|
onceAnEventWithTwoArgs(),
|
|
catchesErrors(),
|
|
stopListeningAfterCatchingError(),
|
|
onceError(),
|
|
onceWithEventTarget(),
|
|
onceWithEventTargetError(),
|
|
prioritizesEventEmitter(),
|
|
abortSignalBefore(),
|
|
abortSignalAfter(),
|
|
abortSignalAfterEvent(),
|
|
eventTargetAbortSignalBefore(),
|
|
eventTargetAbortSignalAfter(),
|
|
eventTargetAbortSignalAfterEvent(),
|
|
]).then(common.mustCall());
|