diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js index df08875cc79ae6..f7f06674ef7c41 100644 --- a/lib/internal/readline/interface.js +++ b/lib/internal/readline/interface.js @@ -28,6 +28,7 @@ const { StringPrototypeStartsWith, StringPrototypeTrim, Symbol, + SymbolDispose, SymbolAsyncIterator, SafeStringIterator, } = primordials; @@ -325,8 +326,8 @@ function InterfaceConstructor(input, output, completer, terminal) { if (signal.aborted) { process.nextTick(onAborted); } else { - signal.addEventListener('abort', onAborted, { once: true }); - self.once('close', () => signal.removeEventListener('abort', onAborted)); + const disposable = EventEmitter.addAbortListener(signal, onAborted); + self.once('close', disposable[SymbolDispose]); } } diff --git a/lib/readline.js b/lib/readline.js index b9c6f17c52b4b0..5276d9401b4c12 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -30,6 +30,7 @@ const { Promise, PromiseReject, StringPrototypeSlice, + SymbolDispose, } = primordials; const { @@ -95,6 +96,7 @@ const { kWordRight, kWriteToOutput, } = require('internal/readline/interface'); +let addAbortListener; function Interface(input, output, completer, terminal) { if (!(this instanceof Interface)) { @@ -143,15 +145,13 @@ Interface.prototype.question = function question(query, options, cb) { const onAbort = () => { this[kQuestionCancel](); }; - options.signal.addEventListener('abort', onAbort, { once: true }); - const cleanup = () => { - options.signal.removeEventListener('abort', onAbort); - }; + addAbortListener ??= require('events').addAbortListener; + const disposable = addAbortListener(options.signal, onAbort); const originalCb = cb; cb = typeof cb === 'function' ? (answer) => { - cleanup(); + disposable[SymbolDispose](); return originalCb(answer); - } : cleanup; + } : disposable[SymbolDispose]; } if (typeof cb === 'function') { @@ -175,9 +175,10 @@ Interface.prototype.question[promisify.custom] = function question(query, option const onAbort = () => { reject(new AbortError(undefined, { cause: options.signal.reason })); }; - options.signal.addEventListener('abort', onAbort, { once: true }); + addAbortListener ??= require('events').addAbortListener; + const disposable = addAbortListener(options.signal, onAbort); cb = (answer) => { - options.signal.removeEventListener('abort', onAbort); + disposable[SymbolDispose](); resolve(answer); }; } diff --git a/lib/readline/promises.js b/lib/readline/promises.js index 9bfa2aaecd6b44..4c2ce90479ef8f 100644 --- a/lib/readline/promises.js +++ b/lib/readline/promises.js @@ -2,6 +2,7 @@ const { Promise, + SymbolDispose, } = primordials; const { @@ -22,6 +23,7 @@ const { validateAbortSignal } = require('internal/validators'); const { kEmptyObject, } = require('internal/util'); +let addAbortListener; class Interface extends _Interface { // eslint-disable-next-line no-useless-constructor @@ -43,9 +45,11 @@ class Interface extends _Interface { this[kQuestionCancel](); reject(new AbortError(undefined, { cause: options.signal.reason })); }; - options.signal.addEventListener('abort', onAbort, { once: true }); + addAbortListener ??= require('events').addAbortListener; + const disposable = addAbortListener(options.signal, onAbort); + cb = (answer) => { - options.signal.removeEventListener('abort', onAbort); + disposable[SymbolDispose](); resolve(answer); }; }