Repository navigation
Conversation
transformStreamDefaultControllerPerformTransform chained a rejection step onto the result of every transform() call. A non-thenable result cannot reject, so return the shared resolved promise directly and save a promise allocation and a microtask per chunk. The write promise of a transform whose transform() returns a non-thenable now settles one microtask earlier than in the reference implementation. The relative order of all stream events is unchanged. Signed-off-by: Matteo Collina <hello@matteocollina.com>
mcollina
force-pushed
the
webstream-perf-round24
branch
from
November 8, 2026 14:03
3415b51 to
22e9ae8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Round 24 of the webstreams performance work. It is independent of #66702 (no conflicts; either can land first).
Rejection step skipped for non-thenable transform results
transformStreamDefaultControllerPerformTransformchained.then(undefined, rejectionSteps)onto the result of everytransform()call, mirroring the reference implementation'spromiseCall(transformAlgorithm, ...).then(undefined, ...). Whentransform()returns a value that is not a thenable (the commontransform(chunk, controller) { controller.enqueue(chunk); }shape, and the identity transform), that result cannot reject, so the rejection step can never run. The shared resolved promise is now returned directly, saving a promise and a microtask per chunk. Promise and thenable results keep the rejection step unchanged.Observable timing
The write promise of such a transform settles one microtask earlier than in the reference implementation. The relative order of every observable stream event is unchanged: a probe of 8 transformer shapes (sync,
async, returned promise, thenable, throwing, rejecting,controller.error(),controller.terminate()) at both readable high water marks, logging writes, reads,ready,closeand bothclosedpromises, produces the same event sequence on the base and patched builds; only the tick numbers of the sync,error()andterminate()shapes move. This is only observable by counting microtasks.I also tried returning the user's promise for
async transform()(with a non-rethrowing error handler registered first): it measured +0..1.6% because the async function's own promise dominates, so it is not worth the broader timing change.Verification
write/close/abortsucceeding and failing, transformertransform/flush/cancelsucceeding, throwing and rejecting, parked writes at both high water marks,error()/terminate(), thenables,pipeThroughwith and without an abort signal; sync and async variants) records theAsyncLocalStoragestore seen by every user callback and every reaction on the returned promises. The callback → store mapping is identical to the base build in every scenario, and no callback runs outside its context.Benchmarks
benchmark/compare.js, 20 runs, against main:The other 12 rows (creation, js_transfer, the remaining lifecycle kinds, encoding-streams len=1024 decode) are within their confidence intervals. The
async-iteratorrow never creates aTransformStream, so the changed code does not run there; a 30-run recheck of it gives -0.96% (not significant), so it is the expected false positive for 19 comparisons.AI Generated, reviewed by me.