'use strict'; const common = require('../common'); const { once, EventEmitter } = require('events'); const { strictEqual, deepStrictEqual } = require('assert'); class EventTargetMock { constructor() { this.events = {}; } addEventListener = common.mustCall(function(name, listener, options) { if (!(name in this.events)) { this.events[name] = { listeners: [], options }; } this.events[name].listeners.push(listener); }); removeEventListener = common.mustCall(function(name, callback) { if (!(name in this.events)) { return; } const event = this.events[name]; const stack = event.listeners; for (let i = 0, l = stack.length; i < l; i++) { if (stack[i] === callback) { stack.splice(i, 1); if (stack.length === 0) { Reflect.deleteProperty(this.events, name); } return; } } }); dispatchEvent = function(name, ...arg) { if (!(name in this.events)) { return true; } const event = this.events[name]; const stack = event.listeners.slice(); for (let i = 0, l = stack.length; i < l; i++) { stack[i].apply(this, arg); if (event.options.once) { this.removeEventListener(name, stack[i]); } } return !name.defaultPrevented; }; } 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); }); process.on('multipleResolves', common.mustNotCall()); 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 [err] = await once(ee, 'error'); strictEqual(err, expected); strictEqual(ee.listenerCount('error'), 0); strictEqual(ee.listenerCount('myevent'), 0); } async function onceWithEventTarget() { const et = new EventTargetMock(); process.nextTick(() => { et.dispatchEvent('myevent', 42); }); const [ value ] = await once(et, 'myevent'); strictEqual(value, 42); strictEqual(Reflect.has(et.events, 'myevent'), false); } async function onceWithEventTargetTwoArgs() { const et = new EventTargetMock(); process.nextTick(() => { et.dispatchEvent('myevent', 42, 24); }); const value = await once(et, 'myevent'); deepStrictEqual(value, [42, 24]); } async function onceWithEventTargetError() { const et = new EventTargetMock(); const expected = new Error('kaboom'); process.nextTick(() => { et.dispatchEvent('error', expected); }); const [err] = await once(et, 'error'); strictEqual(err, expected); strictEqual(Reflect.has(et.events, 'error'), false); } Promise.all([ onceAnEvent(), onceAnEventWithTwoArgs(), catchesErrors(), stopListeningAfterCatchingError(), onceError(), onceWithEventTarget(), onceWithEventTargetTwoArgs(), onceWithEventTargetError(), ]).then(common.mustCall());