@@ -153,12 +153,14 @@ const kNextSyncBatch = Symbol('kNextSyncBatch');
153153
154154// The method of an iterator returned by from() for an async iterable that
155155// reads like next(), for internal callers that stop reading when they
156- // cancel the normalization (pipeTo(), pull() pipelines, consumers): a
157- // return() or throw() while a read is pending closes the source at once,
158- // as for next(), but does not settle the pending read early. That read
159- // settles when the source's next() does, rejecting with the cancellation
160- // reason, and the caller ignores it. It saves the read a promise of its
161- // own: see createAsyncSourceNormalizer().
156+ // cancel the normalization (pipeTo(), pull() pipelines, consumers). It
157+ // saves the read a promise of its own: see createAsyncSourceNormalizer().
158+ // With `releasable` true, a return() or throw() while a read is pending
159+ // closes the source at once, as for next(), but does not settle the
160+ // pending read early: the read settles when the source's next() does,
161+ // rejecting with the cancellation reason, and the caller ignores it.
162+ // Without it, the caller must not cancel while a read is pending (pipeTo()
163+ // without a signal). The first call decides for all reads.
162164const kNextUncancellable = Symbol ( 'kNextUncancellable' ) ;
163165
164166// Methods of the iterator returned by yieldNormalizationAbortable(), used by
@@ -190,8 +192,8 @@ function createNormalizationIterator(createIterator) {
190192 cancelNormalization ( context , error , true ) ;
191193 return FunctionPrototypeCall ( iterator . throw , iterator , error ) ;
192194 } ,
193- [ kNextUncancellable ] ( ) {
194- return iterator [ kNextUncancellable ] ( ) ;
195+ [ kNextUncancellable ] ( releasable ) {
196+ return iterator [ kNextUncancellable ] ( releasable ) ;
195197 } ,
196198 [ SymbolAsyncIterator ] ( ) {
197199 return this ;
@@ -843,9 +845,13 @@ function createAsyncSourceNormalizer(source, context) {
843845 let valueBatches = null ;
844846 // Whether the source is read with kNextUncancellable: set by the
845847 // kNextUncancellable method, for callers that ignore a read pending when
846- // they cancel the normalization. readReleased: whether a cancellation has
847- // released such a read (see onUncancellableCancelled()).
848+ // they cancel the normalization. releasableReads: whether the caller
849+ // can cancel while a read is pending, so that a cancellation releases it
850+ // (pipeTo() without a signal cannot, and its reads skip that).
851+ // readReleased: whether a cancellation has released such a read (see
852+ // onUncancellableCancelled()).
848853 let uncancellable = false ;
854+ let releasableReads = false ;
849855 let readReleased = false ;
850856 // Whether the source is read with kRead, and the settling functions of
851857 // the read's promise while it is pending.
@@ -923,8 +929,6 @@ function createAsyncSourceNormalizer(source, context) {
923929 // A kReadUncancellable result: handled as the result of the source's
924930 // next() is, in the same reaction.
925931 function onUncancellableResult ( result ) {
926- if ( readReleased ) throw context . reason ;
927- context . resolve = null ;
928932 let iterResult ;
929933 try {
930934 iterResult = iterator [ kToIterResult ] ( result ) ;
@@ -935,18 +939,29 @@ function createAsyncSourceNormalizer(source, context) {
935939 }
936940
937941 function onUncancellableError ( error ) {
938- if ( readReleased ) throw context . reason ;
939- context . resolve = null ;
940942 iterator [ kReadFailed ] ( ) ;
941943 return fail ( error ) ;
942944 }
943945
944- // context.resolve while a kNextUncancellable read is pending: the caller
945- // ignores that read, so it is released rather than waited for. The
946- // normalization ends, the source is closed at once, as for a cancelled
947- // kRead (errors ignored), and the operations queued behind the read,
948- // such as the return() that cancelled it, run now. The read settles when
949- // the source's next() does, rejecting with the cancellation reason.
946+ // The same, for a releasable read.
947+ function onReleasableResult ( result ) {
948+ if ( readReleased ) throw context . reason ;
949+ context . resolve = null ;
950+ return onUncancellableResult ( result ) ;
951+ }
952+
953+ function onReleasableError ( error ) {
954+ if ( readReleased ) throw context . reason ;
955+ context . resolve = null ;
956+ return onUncancellableError ( error ) ;
957+ }
958+
959+ // context.resolve while a releasable kNextUncancellable read is pending:
960+ // the caller ignores that read, so it is released rather than waited for.
961+ // The normalization ends, the source is closed at once, as for a
962+ // cancelled kRead (errors ignored), and the operations queued behind the
963+ // read, such as the return() that cancelled it, run now. The read settles
964+ // when the source's next() does, rejecting with the cancellation reason.
950965 function onUncancellableCancelled ( ) {
951966 context . resolve = null ;
952967 readReleased = true ;
@@ -955,7 +970,7 @@ function createAsyncSourceNormalizer(source, context) {
955970 settled ( ) ;
956971 }
957972
958- function readUncancellable ( ) {
973+ function readReleasable ( ) {
959974 const cancelledBefore = context . cancelled ;
960975 const read = iterator [ kReadUncancellable ] ( ) ;
961976 if ( ! cancelledBefore ) {
@@ -966,8 +981,7 @@ function createAsyncSourceNormalizer(source, context) {
966981 context . resolve = onUncancellableCancelled ;
967982 }
968983 }
969- return PromisePrototypeThen ( read , onUncancellableResult ,
970- onUncancellableError ) ;
984+ return PromisePrototypeThen ( read , onReleasableResult , onReleasableError ) ;
971985 }
972986
973987 // The kRead handlers: the result is handled in the reaction to the
@@ -995,7 +1009,11 @@ function createAsyncSourceNormalizer(source, context) {
9951009 }
9961010
9971011 function pullSource ( ) {
998- if ( uncancellable ) return readUncancellable ( ) ;
1012+ if ( uncancellable ) {
1013+ if ( releasableReads ) return readReleasable ( ) ;
1014+ return PromisePrototypeThen ( iterator [ kReadUncancellable ] ( ) ,
1015+ onUncancellableResult , onUncancellableError ) ;
1016+ }
9991017 if ( readable ) {
10001018 // One promise for the read, which a cancellation rejects, instead of
10011019 // next()'s promise and a reaction to it.
@@ -1090,8 +1108,9 @@ function createAsyncSourceNormalizer(source, context) {
10901108 next ( ) { return run ( doNext ) ; } ,
10911109 return ( value ) { return run ( doReturn , value ) ; } ,
10921110 throw ( error ) { return run ( doThrow , error ) ; } ,
1093- [ kNextUncancellable ] ( ) {
1111+ [ kNextUncancellable ] ( releasable = false ) {
10941112 uncancellable = true ;
1113+ releasableReads = releasable ;
10951114 return run ( doNext ) ;
10961115 } ,
10971116 } , null ) ;
0 commit comments