From fb4052a3a11e8e0b16620765c47c6bd7deb8fff4 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 30 May 2019 17:58:55 +0200 Subject: [PATCH 01/17] events: add EventEmitter.on to async iterate over events Fixes: https://github.com/nodejs/node/issues/27847. --- doc/api/events.md | 31 ++++ lib/events.js | 78 +++++++++ test/parallel/test-event-on-async-iterator.js | 164 ++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 test/parallel/test-event-on-async-iterator.js diff --git a/doc/api/events.md b/doc/api/events.md index 69f309a73bcd34..12e4101d07ec7c 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -886,6 +886,37 @@ Value: `Symbol.for('nodejs.rejection')` See how to write a custom [rejection handler][rejection]. +## events.on(emitter, eventName) + + +* `emitter` {EventEmitter} +* `eventName` {string|symbol} The name of the event being listened for +* Returns: {AsyncIterator} that iterates `eventName` events emitted by the `emitter` + +```js +const { on, EventEmitter } = require('events'); + +(async () => { + const ee = new EventEmitter(); + + // Emit later on + process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); + }); + + for await (const event of on(ee, 'foo')) { + console.log(event); // prints ['bar'] [42] + } +})(); +``` + +Returns an `AsyncIterator` that iterates `eventName` events. It will throw +if the `EventEmitter` emits `'error'`. It removes all listeners when +exiting the loop. + [WHATWG-EventTarget]: https://dom.spec.whatwg.org/#interface-eventtarget [`--trace-warnings`]: cli.html#cli_trace_warnings [`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners diff --git a/lib/events.js b/lib/events.js index c91792cbfbda8a..5d1c0dbe97368c 100644 --- a/lib/events.js +++ b/lib/events.js @@ -29,6 +29,7 @@ const { ObjectCreate, ObjectDefineProperty, ObjectGetPrototypeOf, + ObjectSetPrototypeOf, ObjectKeys, Promise, ReflectApply, @@ -62,6 +63,7 @@ function EventEmitter(opts) { } module.exports = EventEmitter; module.exports.once = once; +module.exports.on = on; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; @@ -657,3 +659,79 @@ function once(emitter, name) { emitter.once(name, eventListener); }); } + +const AsyncIteratorPrototype = ObjectGetPrototypeOf( + ObjectGetPrototypeOf(async function* () {}).prototype); + +function createIterResult(value, done) { + return { value, done }; +} + +function on(emitter, event) { + const unconsumedEvents = []; + const unconsumedPromises = []; + let error = null; + + const iterator = ObjectSetPrototypeOf({ + next() { + if (error) { + return Promise.reject(error); + } + + const value = unconsumedEvents.shift(); + if (value) { + return Promise.resolve(createIterResult(value, false)); + } + + return new Promise(function(resolve, reject) { + unconsumedPromises.push({ resolve, reject }); + }); + }, + + return() { + emitter.removeListener(event, eventHandler); + emitter.removeListener('error', errorHandler); + + return Promise.resolve(createIterResult(undefined, true)); + }, + + throw(err) { + if (!err || !(err instanceof Error)) { + throw new ERR_INVALID_ARG_TYPE('EventEmitter.AsyncIterator', + 'Error', err); + } + error = err; + emitter.removeListener(event, eventHandler); + emitter.removeListener('error', errorHandler); + }, + + [Symbol.asyncIterator]() { + return this; + } + }, AsyncIteratorPrototype); + + emitter.on(event, eventHandler); + emitter.on('error', errorHandler); + + return iterator; + + function eventHandler(...args) { + const promise = unconsumedPromises.shift(); + if (promise) { + promise.resolve(createIterResult(args, false)); + } else { + unconsumedEvents.push(args); + } + } + + function errorHandler(err) { + const promise = unconsumedPromises.shift(); + if (promise) { + promise.reject(err); + } else { + emitter.removeListener(event, eventHandler); + emitter.removeListener('error', errorHandler); + error = err; + } + } +} diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js new file mode 100644 index 00000000000000..b6815d9f3f1807 --- /dev/null +++ b/test/parallel/test-event-on-async-iterator.js @@ -0,0 +1,164 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { on, EventEmitter } = require('events'); + +async function basic() { + const ee = new EventEmitter(); + process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); + }); + + const iterable = on(ee, 'foo'); + + const expected = [['bar'], [42]]; + + for await (const event of iterable) { + const current = expected.shift(); + + assert.deepStrictEqual(current, event); + + if (expected.length === 0) { + break; + } + } + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function error() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + process.nextTick(() => { + ee.emit('error', _err); + }); + + const iterable = on(ee, 'foo'); + let looped = false; + let thrown = false; + + try { + // eslint-disable-next-line no-unused-vars + for await (const event of iterable) { + looped = true; + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(looped, false); +} + +async function errorDelayed() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + process.nextTick(() => { + ee.emit('foo', 42); + ee.emit('error', _err); + }); + + const iterable = on(ee, 'foo'); + const expected = [[42]]; + let thrown = false; + + try { + for await (const event of iterable) { + const current = expected.shift(); + assert.deepStrictEqual(current, event); + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function throwInLoop() { + const ee = new EventEmitter(); + const _err = new Error('kaboom'); + + process.nextTick(() => { + ee.emit('foo', 42); + }); + + try { + for await (const event of on(ee, 'foo')) { + assert.deepStrictEqual(event, [42]); + throw _err; + } + } catch (err) { + assert.strictEqual(err, _err); + } + + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function next() { + const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + process.nextTick(function() { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); + }); + const results = await Promise.all([ + iterable.next(), + iterable.next() + ]); + assert.deepStrictEqual(results, [{ + value: ['bar'], + done: false + }, { + value: [42], + done: false + }]); +} + +async function iterableThrow() { + const ee = new EventEmitter(); + process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); // lost in the queue + }); + + const iterable = on(ee, 'foo'); + const _err = new Error('kaboom'); + let thrown = false; + + try { + for await (const event of iterable) { + assert.deepStrictEqual(event, ['bar']); + assert.throws(() => { + // No argument + iterable.throw(); + }, { + message: 'The "EventEmitter.AsyncIterator" property must be' + + ' of type Error. Received type undefined', + name: 'TypeError' + }); + iterable.throw(_err); + } + } catch (err) { + thrown = true; + assert.strictEqual(err, _err); + } + assert.strictEqual(thrown, true); + assert.strictEqual(ee.listenerCount('foo'), 0); + assert.strictEqual(ee.listenerCount('error'), 0); +} + +async function run() { + await basic(); + await error(); + await errorDelayed(); + await throwInLoop(); + await next(); + await iterableThrow(); +} + +run().then(common.mustCall()); From 0a1b52d7fa8b99383b67d2bdd24ac8605c38ff37 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:17:37 +0100 Subject: [PATCH 02/17] Update lib/events.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- lib/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/events.js b/lib/events.js index 5d1c0dbe97368c..a888cdb7235b9f 100644 --- a/lib/events.js +++ b/lib/events.js @@ -675,7 +675,7 @@ function on(emitter, event) { const iterator = ObjectSetPrototypeOf({ next() { if (error) { - return Promise.reject(error); + return PromiseReject(error); } const value = unconsumedEvents.shift(); From 31bb8237b987d67c5b4ed3305a58ede1f963d836 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:18:05 +0100 Subject: [PATCH 03/17] Update lib/events.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- lib/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/events.js b/lib/events.js index a888cdb7235b9f..e1afe6947f8024 100644 --- a/lib/events.js +++ b/lib/events.js @@ -680,7 +680,7 @@ function on(emitter, event) { const value = unconsumedEvents.shift(); if (value) { - return Promise.resolve(createIterResult(value, false)); + return PromiseResolve(createIterResult(value, false)); } return new Promise(function(resolve, reject) { From c795045bb409d0c7a1874aae9972f34cabc26393 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:18:16 +0100 Subject: [PATCH 04/17] Update lib/events.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- lib/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/events.js b/lib/events.js index e1afe6947f8024..201bd9912f2f3b 100644 --- a/lib/events.js +++ b/lib/events.js @@ -705,7 +705,7 @@ function on(emitter, event) { emitter.removeListener('error', errorHandler); }, - [Symbol.asyncIterator]() { + [SymbolAsyncIterator]() { return this; } }, AsyncIteratorPrototype); From 7344bf7190cd5738a29c1ef2d31d8679451cd647 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:18:30 +0100 Subject: [PATCH 05/17] Update lib/events.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- lib/events.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/events.js b/lib/events.js index 201bd9912f2f3b..995ab1d90e25ff 100644 --- a/lib/events.js +++ b/lib/events.js @@ -32,6 +32,8 @@ const { ObjectSetPrototypeOf, ObjectKeys, Promise, + PromiseReject, + PromiseResolve, ReflectApply, ReflectOwnKeys, Symbol, From c8b16cfa786b6af0161445368ac53b41415c3bd0 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:18:39 +0100 Subject: [PATCH 06/17] Update lib/events.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- lib/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/events.js b/lib/events.js index 995ab1d90e25ff..46a8a9f4d16252 100644 --- a/lib/events.js +++ b/lib/events.js @@ -694,7 +694,7 @@ function on(emitter, event) { emitter.removeListener(event, eventHandler); emitter.removeListener('error', errorHandler); - return Promise.resolve(createIterResult(undefined, true)); + return PromiseResolve(createIterResult(undefined, true)); }, throw(err) { From 4542f50d56c097abbbcc22302a6656461845863b Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:19:10 +0100 Subject: [PATCH 07/17] Update test/parallel/test-event-on-async-iterator.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- test/parallel/test-event-on-async-iterator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index b6815d9f3f1807..29832f8bc442c6 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -107,6 +107,7 @@ async function next() { ee.emit('foo', 42); }); const results = await Promise.all([ + iterable.next(), iterable.next(), iterable.next() ]); From 10821a2e20cf27ae9889f0bd9ee24bd93b1bfb32 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:19:22 +0100 Subject: [PATCH 08/17] Update test/parallel/test-event-on-async-iterator.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michaël Zasso --- test/parallel/test-event-on-async-iterator.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index 29832f8bc442c6..eae6387377ce68 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -117,6 +117,9 @@ async function next() { }, { value: [42], done: false + }, { + value: undefined, + done: true }]); } From b71f016285d82fb96b67e3d743692aaed99dd351 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:41:07 +0100 Subject: [PATCH 09/17] fixup: passing tests --- lib/events.js | 12 +++-- test/parallel/test-event-on-async-iterator.js | 44 ++++++++++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/events.js b/lib/events.js index 46a8a9f4d16252..132fe048669401 100644 --- a/lib/events.js +++ b/lib/events.js @@ -38,6 +38,7 @@ const { ReflectOwnKeys, Symbol, SymbolFor, + SymbolAsyncIterator } = primordials; const kRejection = SymbolFor('nodejs.rejection'); @@ -694,6 +695,10 @@ function on(emitter, event) { emitter.removeListener(event, eventHandler); emitter.removeListener('error', errorHandler); + for (const promise of unconsumedPromises) { + promise.resolve(createIterResult(undefined, true)); + } + return PromiseResolve(createIterResult(undefined, true)); }, @@ -727,9 +732,10 @@ function on(emitter, event) { } function errorHandler(err) { - const promise = unconsumedPromises.shift(); - if (promise) { - promise.reject(err); + if (unconsumedPromises.length > 0) { + for (const promise of unconsumedPromises) { + promise.reject(err); + } } else { emitter.removeListener(event, eventHandler); emitter.removeListener('error', errorHandler); diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index eae6387377ce68..d2b4e2b1e33f5f 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -105,6 +105,7 @@ async function next() { process.nextTick(function() { ee.emit('foo', 'bar'); ee.emit('foo', 42); + iterable.return(); }); const results = await Promise.all([ iterable.next(), @@ -123,6 +124,30 @@ async function next() { }]); } +async function nextError() { + const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + const _err = new Error('kaboom'); + process.nextTick(function() { + ee.emit('error', _err); + }); + const results = await Promise.allSettled([ + iterable.next(), + iterable.next(), + iterable.next() + ]); + assert.deepStrictEqual(results, [{ + status: 'rejected', + reason: _err + }, { + status: 'rejected', + reason: _err + }, { + status: 'rejected', + reason: _err + }]); +} + async function iterableThrow() { const ee = new EventEmitter(); process.nextTick(() => { @@ -157,12 +182,19 @@ async function iterableThrow() { } async function run() { - await basic(); - await error(); - await errorDelayed(); - await throwInLoop(); - await next(); - await iterableThrow(); + const funcs = [ + basic, + error, + errorDelayed, + throwInLoop, + next, + nextError, + iterableThrow + ] + + for (const fn of funcs) { + await fn() + } } run().then(common.mustCall()); From 0c3879951a2e706a302fd231b33a9ff4c93c5f5b Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:45:29 +0100 Subject: [PATCH 10/17] fixup: docs updates --- doc/api/events.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/api/events.md b/doc/api/events.md index 12e4101d07ec7c..0b031d6f38f050 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -908,6 +908,9 @@ const { on, EventEmitter } = require('events'); }); for await (const event of on(ee, 'foo')) { + // The execution of this inner block is synchronous and it + // process one event at a time (even with await). Do not use + // if parallel execution is required. console.log(event); // prints ['bar'] [42] } })(); @@ -915,7 +918,8 @@ const { on, EventEmitter } = require('events'); Returns an `AsyncIterator` that iterates `eventName` events. It will throw if the `EventEmitter` emits `'error'`. It removes all listeners when -exiting the loop. +exiting the loop. The `value` returned by each iteration is an array +composed of the emitted event arguments. [WHATWG-EventTarget]: https://dom.spec.whatwg.org/#interface-eventtarget [`--trace-warnings`]: cli.html#cli_trace_warnings From 0e9b7faf79efc6a94899dc597900d8db500b4017 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 09:47:00 +0100 Subject: [PATCH 11/17] fixup: more feedback --- test/parallel/test-event-on-async-iterator.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index d2b4e2b1e33f5f..705c28184aecf9 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -8,6 +8,9 @@ async function basic() { const ee = new EventEmitter(); process.nextTick(() => { ee.emit('foo', 'bar'); + // 'bar' is a spurious event, we are testing + // that it does not show up in the iterable + ee.emit('bar', 24); ee.emit('foo', 42); }); From f1babd5275b034fd89d8e18cc4881e2fea2f593b Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Dec 2019 11:33:28 +0100 Subject: [PATCH 12/17] fixup: reviews --- lib/events.js | 20 +++++++++++-------- test/parallel/test-event-on-async-iterator.js | 13 ++++++++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/events.js b/lib/events.js index 132fe048669401..18eedf45150aa9 100644 --- a/lib/events.js +++ b/lib/events.js @@ -674,6 +674,7 @@ function on(emitter, event) { const unconsumedEvents = []; const unconsumedPromises = []; let error = null; + let finished = false; const iterator = ObjectSetPrototypeOf({ next() { @@ -681,6 +682,10 @@ function on(emitter, event) { return PromiseReject(error); } + if (finished) { + return PromiseResolve(createIterResult(undefined, true)); + } + const value = unconsumedEvents.shift(); if (value) { return PromiseResolve(createIterResult(value, false)); @@ -694,6 +699,7 @@ function on(emitter, event) { return() { emitter.removeListener(event, eventHandler); emitter.removeListener('error', errorHandler); + finished = true; for (const promise of unconsumedPromises) { promise.resolve(createIterResult(undefined, true)); @@ -732,14 +738,12 @@ function on(emitter, event) { } function errorHandler(err) { - if (unconsumedPromises.length > 0) { - for (const promise of unconsumedPromises) { - promise.reject(err); - } - } else { - emitter.removeListener(event, eventHandler); - emitter.removeListener('error', errorHandler); - error = err; + for (const promise of unconsumedPromises) { + promise.reject(err); } + + emitter.removeListener(event, eventHandler); + emitter.removeListener('error', errorHandler); + error = err; } } diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index 705c28184aecf9..e32d67cf8472fb 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -105,16 +105,19 @@ async function throwInLoop() { async function next() { const ee = new EventEmitter(); const iterable = on(ee, 'foo'); + process.nextTick(function() { ee.emit('foo', 'bar'); ee.emit('foo', 42); iterable.return(); }); + const results = await Promise.all([ iterable.next(), iterable.next(), iterable.next() ]); + assert.deepStrictEqual(results, [{ value: ['bar'], done: false @@ -125,6 +128,11 @@ async function next() { value: undefined, done: true }]); + + assert.deepStrictEqual(await iterable.next(), { + value: undefined, + done: true + }); } async function nextError() { @@ -149,6 +157,7 @@ async function nextError() { status: 'rejected', reason: _err }]); + assert.strictEqual(ee.listeners('error').length, 0); } async function iterableThrow() { @@ -193,10 +202,10 @@ async function run() { next, nextError, iterableThrow - ] + ]; for (const fn of funcs) { - await fn() + await fn(); } } From 2e9ffeed233b0d04a0deaf60b7240b7effad223e Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sat, 21 Dec 2019 00:31:46 +0100 Subject: [PATCH 13/17] fixup: address comments --- lib/events.js | 36 +++++++++++----- test/parallel/test-event-on-async-iterator.js | 41 ++++++++++++------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/lib/events.js b/lib/events.js index 18eedf45150aa9..503258f9f67838 100644 --- a/lib/events.js +++ b/lib/events.js @@ -678,19 +678,28 @@ function on(emitter, event) { const iterator = ObjectSetPrototypeOf({ next() { + // First, we consume all unread events + const value = unconsumedEvents.shift(); + if (value) { + return PromiseResolve(createIterResult(value, false)); + } + + // Then we error, if an error happened + // This happens one time if at all, because after 'error' + // we stop listening if (error) { - return PromiseReject(error); + const p = PromiseReject(error); + // Only the first element errors + error = null; + return p; } + // If the iterator is finished, resolve to done if (finished) { return PromiseResolve(createIterResult(undefined, true)); } - const value = unconsumedEvents.shift(); - if (value) { - return PromiseResolve(createIterResult(value, false)); - } - + // Wait until an event happens return new Promise(function(resolve, reject) { unconsumedPromises.push({ resolve, reject }); }); @@ -738,12 +747,17 @@ function on(emitter, event) { } function errorHandler(err) { - for (const promise of unconsumedPromises) { - promise.reject(err); + finished = true; + + const toError = unconsumedPromises.shift(); + + if (toError) { + toError.reject(err); + } else { + // The next time we call next() + error = err; } - emitter.removeListener(event, eventHandler); - emitter.removeListener('error', errorHandler); - error = err; + iterator.return(); } } diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index e32d67cf8472fb..e8e97b1b29ea73 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -151,44 +151,55 @@ async function nextError() { status: 'rejected', reason: _err }, { - status: 'rejected', - reason: _err + status: 'fulfilled', + value: { + value: undefined, + done: true + } }, { - status: 'rejected', - reason: _err + status: 'fulfilled', + value: { + value: undefined, + done: true + } }]); assert.strictEqual(ee.listeners('error').length, 0); } async function iterableThrow() { const ee = new EventEmitter(); + const iterable = on(ee, 'foo'); + process.nextTick(() => { ee.emit('foo', 'bar'); ee.emit('foo', 42); // lost in the queue + iterable.throw(_err); }); - const iterable = on(ee, 'foo'); const _err = new Error('kaboom'); let thrown = false; + assert.throws(() => { + // No argument + iterable.throw(); + }, { + message: 'The "EventEmitter.AsyncIterator" property must be' + + ' of type Error. Received type undefined', + name: 'TypeError' + }); + + const expected = [['bar'], [42]] + try { for await (const event of iterable) { - assert.deepStrictEqual(event, ['bar']); - assert.throws(() => { - // No argument - iterable.throw(); - }, { - message: 'The "EventEmitter.AsyncIterator" property must be' + - ' of type Error. Received type undefined', - name: 'TypeError' - }); - iterable.throw(_err); + assert.deepStrictEqual(event, expected.shift()); } } catch (err) { thrown = true; assert.strictEqual(err, _err); } assert.strictEqual(thrown, true); + assert.strictEqual(expected.length, 0); assert.strictEqual(ee.listenerCount('foo'), 0); assert.strictEqual(ee.listenerCount('error'), 0); } From 346fa56be77aa000f0ed295a83ab697a94fe45d2 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sat, 21 Dec 2019 10:18:33 +0100 Subject: [PATCH 14/17] fixup: linting --- test/parallel/test-event-on-async-iterator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index e8e97b1b29ea73..331beb6d3afe74 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -188,7 +188,7 @@ async function iterableThrow() { name: 'TypeError' }); - const expected = [['bar'], [42]] + const expected = [['bar'], [42]]; try { for await (const event of iterable) { From 0e518be387796a2744eda7953c88b889dbfea13e Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sun, 22 Dec 2019 14:39:58 +0100 Subject: [PATCH 15/17] fixup: fix tests after rebase: --- test/parallel/test-event-on-async-iterator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index 331beb6d3afe74..ff5d8cdaf2aea0 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -184,7 +184,7 @@ async function iterableThrow() { iterable.throw(); }, { message: 'The "EventEmitter.AsyncIterator" property must be' + - ' of type Error. Received type undefined', + ' an instance of Error. Received undefined', name: 'TypeError' }); From c7cda016b88accffef14b901c315bab3f158f1fe Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 23 Dec 2019 00:18:12 +0100 Subject: [PATCH 16/17] Update doc/api/events.md Co-Authored-By: Benjamin Gruenbaum --- doc/api/events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/events.md b/doc/api/events.md index 0b031d6f38f050..6963ca10df0764 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -910,7 +910,7 @@ const { on, EventEmitter } = require('events'); for await (const event of on(ee, 'foo')) { // The execution of this inner block is synchronous and it // process one event at a time (even with await). Do not use - // if parallel execution is required. + // if concurrent execution is required. console.log(event); // prints ['bar'] [42] } })(); From 35939a512a8dda41cbd9b12f574fe16ea81f589f Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 23 Dec 2019 00:18:21 +0100 Subject: [PATCH 17/17] Update doc/api/events.md Co-Authored-By: Benjamin Gruenbaum --- doc/api/events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/events.md b/doc/api/events.md index 6963ca10df0764..1e2a7660cb901b 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -909,7 +909,7 @@ const { on, EventEmitter } = require('events'); for await (const event of on(ee, 'foo')) { // The execution of this inner block is synchronous and it - // process one event at a time (even with await). Do not use + // processes one event at a time (even with await). Do not use // if concurrent execution is required. console.log(event); // prints ['bar'] [42] }