@@ -12,6 +12,7 @@ const {
1212 ArrayIsArray,
1313 ArrayPrototypePush,
1414 ArrayPrototypeSlice,
15+ FunctionPrototypeCall,
1516 PromisePrototypeThen,
1617 PromiseResolve,
1718 SymbolAsyncIterator,
@@ -896,6 +897,76 @@ function pull(source, ...args) {
896897 } ;
897898}
898899
900+ // Keep ownership of a bonded consumer outside the transform pipeline so it can
901+ // be detached even when the pipeline never starts or terminates early.
902+ function pullWithConsumerCleanup ( source , transforms , signal ) {
903+ const sourceIterator = source [ SymbolAsyncIterator ] ( ) ;
904+ const pipelineSource = {
905+ __proto__ : null ,
906+ [ SymbolAsyncIterator ] ( ) {
907+ return sourceIterator ;
908+ } ,
909+ } ;
910+ const pipeline = signal === undefined ?
911+ pull ( pipelineSource , ...transforms ) :
912+ pull ( pipelineSource , ...transforms , { __proto__ : null , signal } ) ;
913+ let sourceClosed = false ;
914+ let abortHandler ;
915+
916+ function closeSource ( method , value ) {
917+ if ( sourceClosed ) return ;
918+ sourceClosed = true ;
919+ if ( abortHandler !== undefined ) {
920+ signal . removeEventListener ( 'abort' , abortHandler ) ;
921+ }
922+ const close = sourceIterator [ method ] ?? sourceIterator . return ;
923+ if ( typeof close === 'function' ) {
924+ const result = FunctionPrototypeCall ( close , sourceIterator , value ) ;
925+ PromisePrototypeThen ( PromiseResolve ( result ) , undefined , ( ) => { } ) ;
926+ }
927+ }
928+
929+ if ( signal !== undefined ) {
930+ abortHandler = ( ) => closeSource ( 'throw' , signal . reason ) ;
931+ signal . addEventListener ( 'abort' , abortHandler ,
932+ { __proto__ : null , once : true } ) ;
933+ if ( signal . aborted ) abortHandler ( ) ;
934+ }
935+
936+ return {
937+ __proto__ : null ,
938+ [ SymbolAsyncIterator ] ( ) {
939+ const iterator = pipeline [ SymbolAsyncIterator ] ( ) ;
940+ return {
941+ __proto__ : null ,
942+ next ( value ) {
943+ return PromisePrototypeThen (
944+ iterator . next ( value ) ,
945+ ( result ) => {
946+ if ( result . done ) closeSource ( 'return' ) ;
947+ return result ;
948+ } ,
949+ ( error ) => {
950+ closeSource ( 'throw' , error ) ;
951+ throw error ;
952+ } ) ;
953+ } ,
954+ return ( value ) {
955+ closeSource ( 'return' , value ) ;
956+ return iterator . return ( value ) ;
957+ } ,
958+ throw ( error ) {
959+ closeSource ( 'throw' , error ) ;
960+ return iterator . throw ( error ) ;
961+ } ,
962+ [ SymbolAsyncIterator ] ( ) {
963+ return this ;
964+ } ,
965+ } ;
966+ } ,
967+ } ;
968+ }
969+
899970// =============================================================================
900971// Public API: pipeTo() and pipeToSync()
901972// =============================================================================
@@ -1112,4 +1183,5 @@ module.exports = {
11121183 pipeToSync,
11131184 pull,
11141185 pullSync,
1186+ pullWithConsumerCleanup,
11151187} ;
0 commit comments