From ad98cf04f3ae0ae17602627ebd4a934a046e707a Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 2 Nov 2020 16:34:24 +0100 Subject: [PATCH 1/7] stream: remove isPromise utility function The function was not checking if the parameter was actually a Promise instance, but if it has a `then` method. Removing the utility function in favor of a clearer `typeof` check, handling the case when the thenable throws if then method is accessed more than once. PR-URL: https://github.com/nodejs/node/pull/35925 Reviewed-By: Robert Nagy Reviewed-By: Benjamin Gruenbaum Reviewed-By: Matteo Collina --- lib/internal/streams/pipeline.js | 20 +++++++++++--------- test/parallel/test-stream-pipeline.js | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index c662bce8db1ab3..a73e17f1f29eed 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -5,8 +5,9 @@ const { ArrayIsArray, + ReflectApply, SymbolAsyncIterator, - SymbolIterator + SymbolIterator, } = primordials; let eos; @@ -77,10 +78,6 @@ function popCallback(streams) { return streams.pop(); } -function isPromise(obj) { - return !!(obj && typeof obj.then === 'function'); -} - function isReadable(obj) { return !!(obj && typeof obj.pipe === 'function'); } @@ -222,14 +219,19 @@ function pipeline(...streams) { const pt = new PassThrough({ objectMode: true }); - if (isPromise(ret)) { - ret - .then((val) => { + + // Handle Promises/A+ spec, `then` could be a getter that throws on + // second use. + const then = ret?.then; + if (typeof then === 'function') { + ReflectApply(then, ret, [ + (val) => { value = val; pt.end(val); }, (err) => { pt.destroy(err); - }); + } + ]); } else if (isIterable(ret, true)) { finishCount++; pump(ret, pt, finish); diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index b7d3720d0d05dd..23887122fc78d8 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1240,3 +1240,24 @@ const net = require('net'); }), ); } +{ + function createThenable() { + let counter = 0; + return { + get then() { + if (counter++) { + throw new Error('Cannot access `then` more than once'); + } + return Function.prototype; + }, + }; + } + + pipeline( + function* () { + yield 0; + }, + createThenable, + () => common.mustNotCall(), + ); +} From 1a6d4dce6db09cb58cb77714cdcb28d84a7c7073 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Fri, 6 Nov 2020 12:05:08 +0200 Subject: [PATCH 2/7] events: getEventListeners static PR-URL: https://github.com/nodejs/node/pull/35991 Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/events.md | 34 ++++++++++++++++++ lib/events.js | 26 +++++++++++++- lib/internal/event_target.js | 2 ++ .../test-events-static-geteventlisteners.js | 36 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-events-static-geteventlisteners.js diff --git a/doc/api/events.md b/doc/api/events.md index 17420a249be731..b593cfac44969f 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -829,6 +829,40 @@ class MyClass extends EventEmitter { } ``` +## `events.getEventListeners(emitterOrTarget, eventName)` + +* `emitterOrTarget` {EventEmitter|EventTarget} +* `eventName` {string|symbol} +* Returns: {Function[]} + +Returns a copy of the array of listeners for the event named `eventName`. + +For `EventEmitter`s this behaves exactly the same as calling `.listeners` on +the emitter. + +For `EventTarget`s this is the only way to get the event listeners for the +event target. This is useful for debugging and diagnostic purposes. + +```js +const { getEventListeners, EventEmitter } = require('events'); + +{ + const ee = new EventEmitter(); + const listener = () => console.log('Events are fun'); + ee.on('foo', listener); + getEventListeners(ee, 'foo'); // [listener] +} +{ + const et = new EventTarget(); + const listener = () => console.log('Events are fun'); + ee.addEventListener('foo', listener); + getEventListeners(ee, 'foo'); // [listener] +} +``` + ## `events.once(emitter, name[, options])`