55 ObjectDefineProperties,
66 ObjectSetPrototypeOf,
77 PromisePrototypeThen,
8- PromiseReject,
9- PromiseResolve,
108 PromiseWithResolvers,
119 Symbol,
1210 SymbolToStringTag,
@@ -46,14 +44,12 @@ const {
4644
4745const {
4846 createPromiseCallback1Param,
49- createRawCallback2Params ,
47+ createPromiseCallback2Params ,
5048 customInspect,
5149 extractHighWaterMark,
5250 extractSizeAlgorithm,
5351 getNonWritablePropertyDescriptor,
5452 isBrandCheck,
55- kParkedAlgorithmResult,
56- kResolvedPromise,
5753 kState,
5854 kType,
5955 nonOpCancel,
@@ -261,12 +257,7 @@ class TransformStreamState {
261257 writable = undefined ;
262258 controller = undefined ;
263259 backpressure = undefined ;
264- // Continuation slots replacing the spec's
265- // [[backpressureChangePromise]]; see transformStreamSetBackpressure.
266- pullPending = false ;
267- pendingWrite = undefined ;
268- pendingWriteChunk = undefined ;
269- writeContinuation = undefined ;
260+ backpressureChange = undefined ;
270261}
271262ObjectSetPrototypeOf ( TransformStreamState . prototype , null ) ;
272263
@@ -372,9 +363,7 @@ const isTransformStream =
372363const isTransformStreamDefaultController =
373364 isBrandCheck ( 'TransformStreamDefaultController' ) ;
374365
375- // Raw callback (see createRawCallback*): invoked inside the try/catch of
376- // transformStreamDefaultControllerPerformTransform.
377- function defaultTransformAlgorithm ( chunk , controller ) {
366+ async function defaultTransformAlgorithm ( chunk , controller ) {
378367 transformStreamDefaultControllerEnqueue ( controller , chunk ) ;
379368}
380369
@@ -444,30 +433,24 @@ function transformStreamUnblockWrite(stream) {
444433// The spec's [[backpressureChangePromise]] is only ever observed by the
445434// source pull algorithm (settles when backpressure next becomes true) and
446435// by a sink write arriving while backpressure is set (settles when
447- // backpressure next becomes false). Both observers are internal, so the
448- // promise record is replaced by continuation slots: a parked pull is
449- // completed by delivering the readable controller's pull-fulfilled step,
450- // and a parked write by the cached write continuation (see
451- // transformStreamDefaultSinkWriteAlgorithm). Each is enqueued on the
452- // shared resolved promise at the exact microtask position the old
453- // record's reaction would have had.
436+ // backpressure next becomes false). Instead of allocating a fresh promise
437+ // record on every flip, the record is materialized lazily on first
438+ // observation and dropped once settled; flips nobody is waiting on
439+ // allocate nothing.
440+ function transformStreamBackpressureChangePromise ( stream ) {
441+ const state = stream [ kState ] ;
442+ return ( state . backpressureChange ??= PromiseWithResolvers ( ) ) . promise ;
443+ }
444+
454445function transformStreamSetBackpressure ( stream , backpressure ) {
455446 const state = stream [ kState ] ;
456447 assert ( state . backpressure !== backpressure ) ;
457- state . backpressure = backpressure ;
458- if ( backpressure ) {
459- if ( state . pullPending ) {
460- state . pullPending = false ;
461- // The pull-fulfilled step exists: a pull parked it (see
462- // transformStreamDefaultSourcePullAlgorithm), and the readable
463- // controller creates it before invoking the pull algorithm.
464- PromisePrototypeThen (
465- kResolvedPromise ,
466- state . readable [ kState ] . controller [ kState ] . pullFulfilled ) ;
467- }
468- } else if ( state . pendingWrite !== undefined ) {
469- PromisePrototypeThen ( kResolvedPromise , state . writeContinuation ) ;
448+ const backpressureChange = state . backpressureChange ;
449+ if ( backpressureChange !== undefined ) {
450+ state . backpressureChange = undefined ;
451+ backpressureChange . resolve ( ) ;
470452 }
453+ state . backpressure = backpressure ;
471454}
472455
473456function setupTransformStreamDefaultController (
@@ -495,7 +478,7 @@ function setupTransformStreamDefaultControllerFromTransformer(
495478 const flush = transformer ?. flush ;
496479 const cancel = transformer ?. cancel ;
497480 const transformAlgorithm = transform ?
498- createRawCallback2Params ( 'transformer.transform' , transform , transformer ) :
481+ createPromiseCallback2Params ( 'transformer.transform' , transform , transformer ) :
499482 defaultTransformAlgorithm ;
500483 const flushAlgorithm = flush ?
501484 createPromiseCallback1Param ( 'transformer.flush' , flush , transformer ) :
@@ -548,40 +531,18 @@ function transformStreamDefaultControllerError(controller, error) {
548531 transformStreamError ( controller [ kState ] . stream , error ) ;
549532}
550533
551- // Mirrors the reference implementation's
552- // `promiseCall(transformAlgorithm, ...).then(undefined, rejectionSteps)`:
553- // the returned promise settles one microtask after the (coerced) result
554- // does, and a rejection errors the transform stream before propagating.
555- // The raw transform callback plus the shared resolved promise for
556- // non-thenable results replace the previous async wrapper's two implicit
557- // promises per chunk.
558- function transformStreamDefaultControllerPerformTransform ( controller , chunk ) {
559- const controllerState = controller [ kState ] ;
560- const transformAlgorithm = controllerState . transformAlgorithm ;
561- if ( transformAlgorithm === undefined ) {
562- // Algorithms were cleared by a concurrent cancel/abort/close.
563- return kResolvedPromise ;
564- }
565- let result ;
534+ async function transformStreamDefaultControllerPerformTransform ( controller , chunk ) {
566535 try {
567- result = transformAlgorithm ( chunk , controller ) ;
536+ const transformAlgorithm = controller [ kState ] . transformAlgorithm ;
537+ if ( transformAlgorithm === undefined ) {
538+ // Algorithms were cleared by a concurrent cancel/abort/close.
539+ return ;
540+ }
541+ return await transformAlgorithm ( chunk , controller ) ;
568542 } catch ( error ) {
569- result = PromiseReject ( error ) ;
570- }
571- if ( result === null ||
572- ( typeof result !== 'object' && typeof result !== 'function' ) ) {
573- result = kResolvedPromise ;
574- } else {
575- result = PromiseResolve ( result ) ;
576- }
577- controllerState . performTransformRejected ??= ( error ) => {
578543 transformStreamError ( controller [ kState ] . stream , error ) ;
579544 throw error ;
580- } ;
581- return PromisePrototypeThen (
582- result ,
583- undefined ,
584- controllerState . performTransformRejected ) ;
545+ }
585546}
586547
587548function transformStreamDefaultControllerTerminate ( controller ) {
@@ -602,42 +563,26 @@ function transformStreamDefaultControllerTerminate(controller) {
602563}
603564
604565function transformStreamDefaultSinkWriteAlgorithm ( stream , chunk ) {
605- const state = stream [ kState ] ;
606566 const {
607567 writable,
608568 controller,
609- } = state ;
569+ } = stream [ kState ] ;
610570 assert ( writable [ kState ] . state === 'writable' ) ;
611- if ( state . backpressure ) {
612- // Park the chunk and one promise record; the backpressure -> false
613- // flip delivers the cached continuation (see
614- // transformStreamSetBackpressure) at the same microtask position as
615- // the old [[backpressureChangePromise]] reaction. The continuation
616- // resolves the sink promise with the perform-transform promise, so
617- // adoption reproduces the old derived-chain settle depth exactly.
618- // The writable dispatches a single write at a time, so one pending
619- // slot suffices.
620- assert ( state . pendingWrite === undefined ) ;
621- const pendingWrite = PromiseWithResolvers ( ) ;
622- state . pendingWrite = pendingWrite ;
623- state . pendingWriteChunk = chunk ;
624- state . writeContinuation ??= ( ) => {
625- const pending = state . pendingWrite ;
626- const pendingChunk = state . pendingWriteChunk ;
627- state . pendingWrite = undefined ;
628- state . pendingWriteChunk = undefined ;
629- const writableState = state . writable [ kState ] ;
630- if ( writableState . state === 'erroring' ) {
631- pending . reject ( writableState . storedError ) ;
632- return ;
633- }
634- assert ( writableState . state === 'writable' ) ;
635- pending . resolve (
636- transformStreamDefaultControllerPerformTransform (
571+ if ( stream [ kState ] . backpressure ) {
572+ const backpressureChange = transformStreamBackpressureChangePromise ( stream ) ;
573+ return PromisePrototypeThen (
574+ backpressureChange ,
575+ ( ) => {
576+ const {
577+ writable,
578+ } = stream [ kState ] ;
579+ if ( writable [ kState ] . state === 'erroring' )
580+ throw writable [ kState ] . storedError ;
581+ assert ( writable [ kState ] . state === 'writable' ) ;
582+ return transformStreamDefaultControllerPerformTransform (
637583 controller ,
638- pendingChunk ) ) ;
639- } ;
640- return pendingWrite . promise ;
584+ chunk ) ;
585+ } ) ;
641586 }
642587 return transformStreamDefaultControllerPerformTransform ( controller , chunk ) ;
643588}
@@ -707,15 +652,9 @@ function transformStreamDefaultSinkCloseAlgorithm(stream) {
707652}
708653
709654function transformStreamDefaultSourcePullAlgorithm ( stream ) {
710- const state = stream [ kState ] ;
711- assert ( state . backpressure ) ;
655+ assert ( stream [ kState ] . backpressure ) ;
712656 transformStreamSetBackpressure ( stream , false ) ;
713- // Park the pull: the next backpressure -> true flip delivers the
714- // pull-fulfilled step (see transformStreamSetBackpressure). The old
715- // [[backpressureChangePromise]] this replaces was only ever resolved,
716- // so the parked pull needs no rejection delivery.
717- state . pullPending = true ;
718- return kParkedAlgorithmResult ;
657+ return transformStreamBackpressureChangePromise ( stream ) ;
719658}
720659
721660function transformStreamDefaultSourceCancelAlgorithm ( stream , reason ) {
0 commit comments