1// Flags: --expose-internals --no-warnings --experimental-abortcontroller 2'use strict'; 3 4const common = require('../common'); 5const assert = require('assert'); 6const { on, EventEmitter } = require('events'); 7const { 8 EventTarget, 9 NodeEventTarget, 10 Event 11} = require('internal/event_target'); 12 13async function basic() { 14 const ee = new EventEmitter(); 15 process.nextTick(() => { 16 ee.emit('foo', 'bar'); 17 // 'bar' is a spurious event, we are testing 18 // that it does not show up in the iterable 19 ee.emit('bar', 24); 20 ee.emit('foo', 42); 21 }); 22 23 const iterable = on(ee, 'foo'); 24 25 const expected = [['bar'], [42]]; 26 27 for await (const event of iterable) { 28 const current = expected.shift(); 29 30 assert.deepStrictEqual(current, event); 31 32 if (expected.length === 0) { 33 break; 34 } 35 } 36 assert.strictEqual(ee.listenerCount('foo'), 0); 37 assert.strictEqual(ee.listenerCount('error'), 0); 38} 39 40async function invalidArgType() { 41 assert.throws(() => on({}, 'foo'), common.expectsError({ 42 code: 'ERR_INVALID_ARG_TYPE', 43 name: 'TypeError', 44 })); 45} 46 47async function error() { 48 const ee = new EventEmitter(); 49 const _err = new Error('kaboom'); 50 process.nextTick(() => { 51 ee.emit('error', _err); 52 }); 53 54 const iterable = on(ee, 'foo'); 55 let looped = false; 56 let thrown = false; 57 58 try { 59 // eslint-disable-next-line no-unused-vars 60 for await (const event of iterable) { 61 looped = true; 62 } 63 } catch (err) { 64 thrown = true; 65 assert.strictEqual(err, _err); 66 } 67 assert.strictEqual(thrown, true); 68 assert.strictEqual(looped, false); 69} 70 71async function errorDelayed() { 72 const ee = new EventEmitter(); 73 const _err = new Error('kaboom'); 74 process.nextTick(() => { 75 ee.emit('foo', 42); 76 ee.emit('error', _err); 77 }); 78 79 const iterable = on(ee, 'foo'); 80 const expected = [[42]]; 81 let thrown = false; 82 83 try { 84 for await (const event of iterable) { 85 const current = expected.shift(); 86 assert.deepStrictEqual(current, event); 87 } 88 } catch (err) { 89 thrown = true; 90 assert.strictEqual(err, _err); 91 } 92 assert.strictEqual(thrown, true); 93 assert.strictEqual(ee.listenerCount('foo'), 0); 94 assert.strictEqual(ee.listenerCount('error'), 0); 95} 96 97async function throwInLoop() { 98 const ee = new EventEmitter(); 99 const _err = new Error('kaboom'); 100 101 process.nextTick(() => { 102 ee.emit('foo', 42); 103 }); 104 105 try { 106 for await (const event of on(ee, 'foo')) { 107 assert.deepStrictEqual(event, [42]); 108 throw _err; 109 } 110 } catch (err) { 111 assert.strictEqual(err, _err); 112 } 113 114 assert.strictEqual(ee.listenerCount('foo'), 0); 115 assert.strictEqual(ee.listenerCount('error'), 0); 116} 117 118async function next() { 119 const ee = new EventEmitter(); 120 const iterable = on(ee, 'foo'); 121 122 process.nextTick(function() { 123 ee.emit('foo', 'bar'); 124 ee.emit('foo', 42); 125 iterable.return(); 126 }); 127 128 const results = await Promise.all([ 129 iterable.next(), 130 iterable.next(), 131 iterable.next(), 132 ]); 133 134 assert.deepStrictEqual(results, [{ 135 value: ['bar'], 136 done: false 137 }, { 138 value: [42], 139 done: false 140 }, { 141 value: undefined, 142 done: true 143 }]); 144 145 assert.deepStrictEqual(await iterable.next(), { 146 value: undefined, 147 done: true 148 }); 149} 150 151async function nextError() { 152 const ee = new EventEmitter(); 153 const iterable = on(ee, 'foo'); 154 const _err = new Error('kaboom'); 155 process.nextTick(function() { 156 ee.emit('error', _err); 157 }); 158 const results = await Promise.allSettled([ 159 iterable.next(), 160 iterable.next(), 161 iterable.next(), 162 ]); 163 assert.deepStrictEqual(results, [{ 164 status: 'rejected', 165 reason: _err 166 }, { 167 status: 'fulfilled', 168 value: { 169 value: undefined, 170 done: true 171 } 172 }, { 173 status: 'fulfilled', 174 value: { 175 value: undefined, 176 done: true 177 } 178 }]); 179 assert.strictEqual(ee.listeners('error').length, 0); 180} 181 182async function iterableThrow() { 183 const ee = new EventEmitter(); 184 const iterable = on(ee, 'foo'); 185 186 process.nextTick(() => { 187 ee.emit('foo', 'bar'); 188 ee.emit('foo', 42); // lost in the queue 189 iterable.throw(_err); 190 }); 191 192 const _err = new Error('kaboom'); 193 let thrown = false; 194 195 assert.throws(() => { 196 // No argument 197 iterable.throw(); 198 }, { 199 message: 'The "EventEmitter.AsyncIterator" property must be' + 200 ' an instance of Error. Received undefined', 201 name: 'TypeError' 202 }); 203 204 const expected = [['bar'], [42]]; 205 206 try { 207 for await (const event of iterable) { 208 assert.deepStrictEqual(event, expected.shift()); 209 } 210 } catch (err) { 211 thrown = true; 212 assert.strictEqual(err, _err); 213 } 214 assert.strictEqual(thrown, true); 215 assert.strictEqual(expected.length, 0); 216 assert.strictEqual(ee.listenerCount('foo'), 0); 217 assert.strictEqual(ee.listenerCount('error'), 0); 218} 219 220async function eventTarget() { 221 const et = new EventTarget(); 222 const tick = () => et.dispatchEvent(new Event('tick')); 223 const interval = setInterval(tick, 0); 224 let count = 0; 225 for await (const [ event ] of on(et, 'tick')) { 226 count++; 227 assert.strictEqual(event.type, 'tick'); 228 if (count >= 5) { 229 break; 230 } 231 } 232 assert.strictEqual(count, 5); 233 clearInterval(interval); 234} 235 236async function errorListenerCount() { 237 const et = new EventEmitter(); 238 on(et, 'foo'); 239 assert.strictEqual(et.listenerCount('error'), 1); 240} 241 242async function nodeEventTarget() { 243 const et = new NodeEventTarget(); 244 const tick = () => et.dispatchEvent(new Event('tick')); 245 const interval = setInterval(tick, 0); 246 let count = 0; 247 for await (const [ event] of on(et, 'tick')) { 248 count++; 249 assert.strictEqual(event.type, 'tick'); 250 if (count >= 5) { 251 break; 252 } 253 } 254 assert.strictEqual(count, 5); 255 clearInterval(interval); 256} 257 258async function abortableOnBefore() { 259 const ee = new EventEmitter(); 260 const ac = new AbortController(); 261 ac.abort(); 262 [1, {}, null, false, 'hi'].forEach((signal) => { 263 assert.throws(() => on(ee, 'foo', { signal }), { 264 code: 'ERR_INVALID_ARG_TYPE' 265 }); 266 }); 267 assert.throws(() => on(ee, 'foo', { signal: ac.signal }), { 268 name: 'AbortError' 269 }); 270} 271 272async function eventTargetAbortableOnBefore() { 273 const et = new EventTarget(); 274 const ac = new AbortController(); 275 ac.abort(); 276 [1, {}, null, false, 'hi'].forEach((signal) => { 277 assert.throws(() => on(et, 'foo', { signal }), { 278 code: 'ERR_INVALID_ARG_TYPE' 279 }); 280 }); 281 assert.throws(() => on(et, 'foo', { signal: ac.signal }), { 282 name: 'AbortError' 283 }); 284} 285 286async function abortableOnAfter() { 287 const ee = new EventEmitter(); 288 const ac = new AbortController(); 289 290 const i = setInterval(() => ee.emit('foo', 'foo'), 10); 291 292 async function foo() { 293 for await (const f of on(ee, 'foo', { signal: ac.signal })) { 294 assert.strictEqual(f, 'foo'); 295 } 296 } 297 298 foo().catch(common.mustCall((error) => { 299 assert.strictEqual(error.name, 'AbortError'); 300 })).finally(() => { 301 clearInterval(i); 302 }); 303 304 process.nextTick(() => ac.abort()); 305} 306 307async function eventTargetAbortableOnAfter() { 308 const et = new EventTarget(); 309 const ac = new AbortController(); 310 311 const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); 312 313 async function foo() { 314 for await (const f of on(et, 'foo', { signal: ac.signal })) { 315 assert(f); 316 } 317 } 318 319 foo().catch(common.mustCall((error) => { 320 assert.strictEqual(error.name, 'AbortError'); 321 })).finally(() => { 322 clearInterval(i); 323 }); 324 325 process.nextTick(() => ac.abort()); 326} 327 328async function eventTargetAbortableOnAfter2() { 329 const et = new EventTarget(); 330 const ac = new AbortController(); 331 332 const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10); 333 334 async function foo() { 335 for await (const f of on(et, 'foo', { signal: ac.signal })) { 336 assert(f); 337 // Cancel after a single event has been triggered. 338 ac.abort(); 339 } 340 } 341 342 foo().catch(common.mustCall((error) => { 343 assert.strictEqual(error.name, 'AbortError'); 344 })).finally(() => { 345 clearInterval(i); 346 }); 347} 348 349async function abortableOnAfterDone() { 350 const ee = new EventEmitter(); 351 const ac = new AbortController(); 352 353 const i = setInterval(() => ee.emit('foo', 'foo'), 1); 354 let count = 0; 355 356 async function foo() { 357 for await (const f of on(ee, 'foo', { signal: ac.signal })) { 358 assert.strictEqual(f[0], 'foo'); 359 if (++count === 5) 360 break; 361 } 362 ac.abort(); // No error will occur 363 } 364 365 foo().finally(() => { 366 clearInterval(i); 367 }); 368} 369 370async function run() { 371 const funcs = [ 372 basic, 373 invalidArgType, 374 error, 375 errorDelayed, 376 throwInLoop, 377 next, 378 nextError, 379 iterableThrow, 380 eventTarget, 381 errorListenerCount, 382 nodeEventTarget, 383 abortableOnBefore, 384 abortableOnAfter, 385 eventTargetAbortableOnBefore, 386 eventTargetAbortableOnAfter, 387 eventTargetAbortableOnAfter2, 388 abortableOnAfterDone, 389 ]; 390 391 for (const fn of funcs) { 392 await fn(); 393 } 394} 395 396run().then(common.mustCall()); 397