diff --git a/lib/internal/vfs/watcher.js b/lib/internal/vfs/watcher.js index 0f23c1588b17..f6035abfd8be 100644 --- a/lib/internal/vfs/watcher.js +++ b/lib/internal/vfs/watcher.js @@ -4,6 +4,7 @@ const { ArrayPrototypePush, ObjectAssign, Promise, + PromiseReject, PromiseResolve, SafeMap, SafeSet, @@ -579,6 +580,7 @@ const kMaxPendingEvents = 1024; class VFSWatchAsyncIterable { #watcher; #closed = false; + #abortError = null; #pendingEvents = []; #pendingResolvers = []; @@ -616,14 +618,21 @@ class VFSWatchAsyncIterable { } }); - // Handle abort signal - reject pending next() with AbortError + // Handle abort signal - reject the next pending next() with AbortError, + // then behave like an exhausted iterator, matching native fs.promises.watch. if (signal) { const onAbort = () => { this.#closed = true; const err = new AbortError(undefined, { cause: signal.reason }); - while (this.#pendingResolvers.length > 0) { + if (this.#pendingResolvers.length > 0) { const { reject } = this.#pendingResolvers.shift(); reject(err); + while (this.#pendingResolvers.length > 0) { + const { resolve } = this.#pendingResolvers.shift(); + resolve({ done: true, value: undefined }); + } + } else { + this.#abortError = err; } this.#watcher.close(); }; @@ -648,6 +657,12 @@ class VFSWatchAsyncIterable { * @returns {Promise} */ next() { + if (this.#abortError !== null) { + const err = this.#abortError; + this.#abortError = null; + return PromiseReject(err); + } + if (this.#closed) { return PromiseResolve({ done: true, value: undefined }); } diff --git a/test/parallel/test-vfs-watch-abort-signal.js b/test/parallel/test-vfs-watch-abort-signal.js index 4ceb9f787445..2b62ffe74788 100644 --- a/test/parallel/test-vfs-watch-abort-signal.js +++ b/test/parallel/test-vfs-watch-abort-signal.js @@ -28,11 +28,13 @@ const vfs = require('node:vfs'); setImmediate(() => myVfs.writeFileSync('/file.txt', 'b')); } - // promises.watch with pre-aborted signal resolves done immediately + // promises.watch with pre-aborted signal rejects the first next() with + // AbortError, matching native fs.promises.watch { const myVfs = vfs.create(); myVfs.writeFileSync('/p.txt', 'a'); const iter = myVfs.promises.watch('/p.txt', { signal: AbortSignal.abort() }); + await assert.rejects(iter.next(), { name: 'AbortError', code: 'ABORT_ERR' }); const r = await iter.next(); assert.strictEqual(r.done, true); }