1'use strict'; 2 3const common = require('../common'); 4const { once, EventEmitter } = require('events'); 5const { strictEqual, deepStrictEqual } = require('assert'); 6 7class EventTargetMock { 8 constructor() { 9 this.events = {}; 10 } 11 12 addEventListener = common.mustCall(function(name, listener, options) { 13 if (!(name in this.events)) { 14 this.events[name] = { listeners: [], options }; 15 } 16 this.events[name].listeners.push(listener); 17 }); 18 19 removeEventListener = common.mustCall(function(name, callback) { 20 if (!(name in this.events)) { 21 return; 22 } 23 const event = this.events[name]; 24 const stack = event.listeners; 25 26 for (let i = 0, l = stack.length; i < l; i++) { 27 if (stack[i] === callback) { 28 stack.splice(i, 1); 29 if (stack.length === 0) { 30 Reflect.deleteProperty(this.events, name); 31 } 32 return; 33 } 34 } 35 }); 36 37 dispatchEvent = function(name, ...arg) { 38 if (!(name in this.events)) { 39 return true; 40 } 41 const event = this.events[name]; 42 const stack = event.listeners.slice(); 43 44 for (let i = 0, l = stack.length; i < l; i++) { 45 stack[i].apply(this, arg); 46 if (event.options.once) { 47 this.removeEventListener(name, stack[i]); 48 } 49 } 50 return !name.defaultPrevented; 51 }; 52} 53 54async function onceAnEvent() { 55 const ee = new EventEmitter(); 56 57 process.nextTick(() => { 58 ee.emit('myevent', 42); 59 }); 60 61 const [value] = await once(ee, 'myevent'); 62 strictEqual(value, 42); 63 strictEqual(ee.listenerCount('error'), 0); 64 strictEqual(ee.listenerCount('myevent'), 0); 65} 66 67async function onceAnEventWithTwoArgs() { 68 const ee = new EventEmitter(); 69 70 process.nextTick(() => { 71 ee.emit('myevent', 42, 24); 72 }); 73 74 const value = await once(ee, 'myevent'); 75 deepStrictEqual(value, [42, 24]); 76} 77 78async function catchesErrors() { 79 const ee = new EventEmitter(); 80 81 const expected = new Error('kaboom'); 82 let err; 83 process.nextTick(() => { 84 ee.emit('error', expected); 85 }); 86 87 try { 88 await once(ee, 'myevent'); 89 } catch (_e) { 90 err = _e; 91 } 92 strictEqual(err, expected); 93 strictEqual(ee.listenerCount('error'), 0); 94 strictEqual(ee.listenerCount('myevent'), 0); 95} 96 97async function stopListeningAfterCatchingError() { 98 const ee = new EventEmitter(); 99 100 const expected = new Error('kaboom'); 101 let err; 102 process.nextTick(() => { 103 ee.emit('error', expected); 104 ee.emit('myevent', 42, 24); 105 }); 106 107 process.on('multipleResolves', common.mustNotCall()); 108 109 try { 110 await once(ee, 'myevent'); 111 } catch (_e) { 112 err = _e; 113 } 114 process.removeAllListeners('multipleResolves'); 115 strictEqual(err, expected); 116 strictEqual(ee.listenerCount('error'), 0); 117 strictEqual(ee.listenerCount('myevent'), 0); 118} 119 120async function onceError() { 121 const ee = new EventEmitter(); 122 123 const expected = new Error('kaboom'); 124 process.nextTick(() => { 125 ee.emit('error', expected); 126 }); 127 128 const [err] = await once(ee, 'error'); 129 strictEqual(err, expected); 130 strictEqual(ee.listenerCount('error'), 0); 131 strictEqual(ee.listenerCount('myevent'), 0); 132} 133 134async function onceWithEventTarget() { 135 const et = new EventTargetMock(); 136 137 process.nextTick(() => { 138 et.dispatchEvent('myevent', 42); 139 }); 140 const [ value ] = await once(et, 'myevent'); 141 strictEqual(value, 42); 142 strictEqual(Reflect.has(et.events, 'myevent'), false); 143} 144 145async function onceWithEventTargetTwoArgs() { 146 const et = new EventTargetMock(); 147 148 process.nextTick(() => { 149 et.dispatchEvent('myevent', 42, 24); 150 }); 151 152 const value = await once(et, 'myevent'); 153 deepStrictEqual(value, [42, 24]); 154} 155 156async function onceWithEventTargetError() { 157 const et = new EventTargetMock(); 158 159 const expected = new Error('kaboom'); 160 process.nextTick(() => { 161 et.dispatchEvent('error', expected); 162 }); 163 164 const [err] = await once(et, 'error'); 165 strictEqual(err, expected); 166 strictEqual(Reflect.has(et.events, 'error'), false); 167} 168 169async function assumesEventEmitterIfOnIsAFunction() { 170 const ee = new EventEmitter(); 171 ee.addEventListener = () => {}; 172 173 const expected = new Error('kaboom'); 174 let err; 175 process.nextTick(() => { 176 ee.emit('error', expected); 177 }); 178 179 try { 180 await once(ee, 'myevent'); 181 } catch (_e) { 182 err = _e; 183 } 184 185 strictEqual(err, expected); 186 strictEqual(ee.listenerCount('error'), 0); 187 strictEqual(ee.listenerCount('myevent'), 0); 188} 189 190Promise.all([ 191 onceAnEvent(), 192 onceAnEventWithTwoArgs(), 193 catchesErrors(), 194 stopListeningAfterCatchingError(), 195 onceError(), 196 onceWithEventTarget(), 197 onceWithEventTargetTwoArgs(), 198 onceWithEventTargetError(), 199 assumesEventEmitterIfOnIsAFunction(), 200]).then(common.mustCall()); 201