diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js index b8a44e58bee81c..5dbf1305ebfbcb 100644 --- a/lib/internal/readline/interface.js +++ b/lib/internal/readline/interface.js @@ -29,6 +29,7 @@ const { StringPrototypeStartsWith, StringPrototypeTrim, Symbol, + SymbolDispose, SymbolAsyncIterator, SafeStringIterator, } = primordials; @@ -332,8 +333,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 739785855f1bbb..de43ec45741f14 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -30,6 +30,7 @@ const { Promise, PromiseReject, StringPrototypeSlice, + SymbolDispose, } = primordials; const { @@ -94,6 +95,7 @@ const { kWordRight, kWriteToOutput, } = require('internal/readline/interface'); +let addAbortListener; function Interface(input, output, completer, terminal) { if (!(this instanceof Interface)) { @@ -144,15 +146,13 @@ Interface.prototype.question = function(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') { @@ -176,9 +176,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 728a6253fd5e67..bc526dde43eee7 100644 --- a/lib/readline/promises.js +++ b/lib/readline/promises.js @@ -2,6 +2,7 @@ const { Promise, + SymbolDispose, } = primordials; const { @@ -21,6 +22,7 @@ const { validateAbortSignal } = require('internal/validators'); const { kEmptyObject, } = require('internal/util'); +let addAbortListener; class Interface extends _Interface { // eslint-disable-next-line no-useless-constructor @@ -42,9 +44,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); }; }