diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index 876e3a5bf6e2f0..320ca1452fa30b 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -111,6 +111,7 @@ const { kState, kType, lazyTransfer, + markPromiseAsHandled, nonOpCancel, nonOpPull, nonOpStart, @@ -1569,9 +1570,18 @@ function readableStreamPipeTo( } // Write the chunk - we're already in a separate microtask from enqueue - // because we awaited writer[kState].ready.promise above + // because we awaited writer[kState].ready.promise above. + // + // setPromiseHandled's job here is purely to silence the per-chunk + // unhandled-rejection event — the chain Promise + noop closure it + // would allocate are unobserved by anything else (errors propagate + // through writer.closed, which pipeTo monitors via watchErrored). + // markPromiseAsHandled sets V8's MarkAsHandled + MarkAsSilent flags + // directly, with no per-chunk allocation. The LAST state.currentWrite + // is awaited in waitForCurrentWrite during shutdown — markAsHandled + // does not affect that subsequent .then chain. state.currentWrite = writableStreamDefaultWriterWrite(writer, chunk); - setPromiseHandled(state.currentWrite); + markPromiseAsHandled(state.currentWrite); // Check backpressure after each write if (dest[kState].backpressure) { @@ -1675,7 +1685,10 @@ class PipeToReadableStreamReadRequest { // "ReadableStreamPipeTo" step 15's "chunk steps". queueMicrotask(() => { this.state.currentWrite = writableStreamDefaultWriterWrite(this.writer, chunk); - setPromiseHandled(this.state.currentWrite); + // See comment in pipeTo's fast path on the markPromiseAsHandled vs + // setPromiseHandled choice. The per-chunk silencing has no other + // observer; errors reach pipeTo via writer.closed monitoring. + markPromiseAsHandled(this.state.currentWrite); this.promise.resolve(false); }); } diff --git a/lib/internal/webstreams/util.js b/lib/internal/webstreams/util.js index 808b0b069e57f7..20299685a96ad6 100644 --- a/lib/internal/webstreams/util.js +++ b/lib/internal/webstreams/util.js @@ -35,6 +35,7 @@ const { kPending, }, getPromiseDetails, + markPromiseAsHandled, } = internalBinding('util'); const assert = require('internal/assert'); @@ -225,6 +226,7 @@ module.exports = { kState, kType, lazyTransfer, + markPromiseAsHandled, nonOpCancel, nonOpFlush, nonOpPull,