2323
2424const {
2525 ArrayPrototypeIndexOf,
26+ AsyncIteratorPrototype,
27+ FunctionPrototypeCall,
2628 NumberIsInteger,
2729 NumberIsNaN,
2830 NumberParseInt,
@@ -103,6 +105,7 @@ const FastBuffer = Buffer[SymbolSpecies];
103105
104106const { StringDecoder } = require ( 'string_decoder' ) ;
105107const from = require ( 'internal/streams/from' ) ;
108+ const FixedQueue = require ( 'internal/fixed_queue' ) ;
106109
107110ObjectSetPrototypeOf ( Readable . prototype , Stream . prototype ) ;
108111ObjectSetPrototypeOf ( Readable , Stream ) ;
@@ -1394,6 +1397,7 @@ function createAsyncIterator(stream, options) {
13941397 let completed = false ;
13951398 let inFlight = false ; // An asynchronous request is outstanding
13961399 let queue = null ; // Requests received while inFlight
1400+ let draining = false ;
13971401 let cleanup ;
13981402
13991403 // Used both as the 'readable' listener (where `this === stream`) and
@@ -1450,15 +1454,25 @@ function createAsyncIterator(stream, options) {
14501454 }
14511455
14521456 function drain ( ) {
1453- while ( ! inFlight && queue . length > 0 ) {
1454- const req = queue . shift ( ) ;
1455- if ( req . type === 'next' ) {
1456- processNext ( req . resolve , req . reject ) ;
1457- } else if ( req . type === 'return' ) {
1458- processReturn ( req . value , req . resolve ) ;
1459- } else {
1460- processThrow ( req . value , req . reject ) ;
1457+ // Requests settled synchronously call back into drain(); the guard
1458+ // keeps a single loop going instead of recursing once per request.
1459+ if ( draining ) {
1460+ return ;
1461+ }
1462+ draining = true ;
1463+ try {
1464+ while ( ! inFlight && ! queue . isEmpty ( ) ) {
1465+ const req = queue . shift ( ) ;
1466+ if ( req . type === 'next' ) {
1467+ processNext ( req . resolve , req . reject ) ;
1468+ } else if ( req . type === 'return' ) {
1469+ processReturn ( req . value , req . resolve ) ;
1470+ } else {
1471+ processThrow ( req . value , req . reject ) ;
1472+ }
14611473 }
1474+ } finally {
1475+ draining = false ;
14621476 }
14631477 }
14641478
@@ -1483,8 +1497,11 @@ function createAsyncIterator(stream, options) {
14831497 function pump ( resolve , reject ) {
14841498 const chunk = stream . destroyed ? null : stream . read ( ) ;
14851499 if ( chunk !== null ) {
1486- if ( typeof chunk . then === 'function' ) {
1487- PromisePrototypeThen ( PromiseResolve ( chunk ) , ( value ) => {
1500+ // Read `then` only once so that a getter cannot observe (or throw
1501+ // on) a second access.
1502+ const then = chunk . then ;
1503+ if ( typeof then === 'function' ) {
1504+ FunctionPrototypeCall ( then , chunk , ( value ) => {
14881505 inFlight = false ;
14891506 resolve ( { done : false , value } ) ;
14901507 if ( queue !== null ) drain ( ) ;
@@ -1546,16 +1563,20 @@ function createAsyncIterator(stream, options) {
15461563 }
15471564
15481565 return {
1566+ __proto__ : AsyncIteratorPrototype ,
15491567 next ( ) {
15501568 if ( ! inFlight && ! completed ) {
15511569 if ( ! started ) start ( ) ;
15521570 // Fast path: a chunk is already buffered.
15531571 const chunk = stream . destroyed ? null : stream . read ( ) ;
15541572 if ( chunk !== null ) {
1555- if ( typeof chunk . then === 'function' ) {
1573+ // Read `then` only once so that a getter cannot observe (or
1574+ // throw on) a second access.
1575+ const then = chunk . then ;
1576+ if ( typeof then === 'function' ) {
15561577 inFlight = true ;
1557- return PromisePrototypeThen (
1558- PromiseResolve ( chunk ) , onChunkFulfilled , onChunkRejected ) ;
1578+ return FunctionPrototypeCall (
1579+ then , chunk , onChunkFulfilled , onChunkRejected ) ;
15591580 }
15601581 return PromiseResolve ( { done : false , value : chunk } ) ;
15611582 }
@@ -1575,8 +1596,8 @@ function createAsyncIterator(stream, options) {
15751596 }
15761597 return new Promise ( ( resolve , reject ) => {
15771598 if ( inFlight ) {
1578- queue ??= [ ] ;
1579- queue . push ( { type : 'next' , value : undefined , resolve, reject } ) ;
1599+ queue ??= new FixedQueue ( ) ;
1600+ queue . push ( { __proto__ : null , type : 'next' , value : undefined , resolve, reject } ) ;
15801601 } else {
15811602 resolve ( { done : true , value : undefined } ) ;
15821603 }
@@ -1585,8 +1606,8 @@ function createAsyncIterator(stream, options) {
15851606 return ( value ) {
15861607 return new Promise ( ( resolve , reject ) => {
15871608 if ( inFlight ) {
1588- queue ??= [ ] ;
1589- queue . push ( { type : 'return' , value, resolve, reject } ) ;
1609+ queue ??= new FixedQueue ( ) ;
1610+ queue . push ( { __proto__ : null , type : 'return' , value, resolve, reject } ) ;
15901611 } else {
15911612 processReturn ( value , resolve ) ;
15921613 }
@@ -1595,8 +1616,8 @@ function createAsyncIterator(stream, options) {
15951616 throw ( err ) {
15961617 return new Promise ( ( resolve , reject ) => {
15971618 if ( inFlight ) {
1598- queue ??= [ ] ;
1599- queue . push ( { type : 'throw' , value : err , resolve, reject } ) ;
1619+ queue ??= new FixedQueue ( ) ;
1620+ queue . push ( { __proto__ : null , type : 'throw' , value : err , resolve, reject } ) ;
16001621 } else {
16011622 processThrow ( err , reject ) ;
16021623 }
0 commit comments