diff options
author | James M Snell <jasnell@gmail.com> | 2020-08-24 13:11:23 -0700 |
---|---|---|
committer | Node.js GitHub Bot <github-bot@iojs.org> | 2020-08-31 15:09:57 +0000 |
commit | 883fc779b637732b18e2d0e6b1f386cebb37e93c (patch) | |
tree | 3451371d49699a9ce78550851a535c4bce435f42 /lib/events.js | |
parent | 37a8179673590af10b9e8e413388adffc21ba713 (diff) | |
download | node-new-883fc779b637732b18e2d0e6b1f386cebb37e93c.tar.gz |
events: allow use of AbortController with once
Allows an AbortSignal to be passed in to events.once() to cancel
waiting on an event.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/34911
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'lib/events.js')
-rw-r--r-- | lib/events.js | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/lib/events.js b/lib/events.js index 48341c0b20..270588fcbc 100644 --- a/lib/events.js +++ b/lib/events.js @@ -44,6 +44,7 @@ const kRejection = SymbolFor('nodejs.rejection'); let spliceOne; const { + hideStackFrames, kEnhanceStackBeforeInspector, codes } = require('internal/errors'); @@ -57,9 +58,20 @@ const { inspect } = require('internal/util/inspect'); +const { + validateAbortSignal +} = require('internal/validators'); + const kCapture = Symbol('kCapture'); const kErrorMonitor = Symbol('events.errorMonitor'); +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + function EventEmitter(opts) { EventEmitter.init.call(this, opts); } @@ -621,22 +633,61 @@ function unwrapListeners(arr) { return ret; } -function once(emitter, name) { +async function once(emitter, name, options = {}) { + const signal = options ? options.signal : undefined; + validateAbortSignal(signal, 'options.signal'); + if (signal && signal.aborted) + throw lazyDOMException('The operation was aborted', 'AbortError'); return new Promise((resolve, reject) => { const errorListener = (err) => { emitter.removeListener(name, resolver); + if (signal != null) { + eventTargetAgnosticRemoveListener( + signal, + 'abort', + abortListener, + { once: true }); + } reject(err); }; const resolver = (...args) => { if (typeof emitter.removeListener === 'function') { emitter.removeListener('error', errorListener); } + if (signal != null) { + eventTargetAgnosticRemoveListener( + signal, + 'abort', + abortListener, + { once: true }); + } resolve(args); }; eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); if (name !== 'error') { addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); } + function abortListener() { + if (typeof emitter.removeListener === 'function') { + emitter.removeListener(name, resolver); + emitter.removeListener('error', errorListener); + } else { + eventTargetAgnosticRemoveListener( + emitter, + name, + resolver, + { once: true }); + eventTargetAgnosticRemoveListener( + emitter, + 'error', + errorListener, + { once: true }); + } + reject(lazyDOMException('The operation was aborted', 'AbortError')); + } + if (signal != null) { + signal.addEventListener('abort', abortListener, { once: true }); + } }); } |