@@ -10,7 +10,10 @@ const {
10
10
PromisePrototypeThen,
11
11
PromiseReject,
12
12
PromiseResolve,
13
+ SafeSet,
13
14
Symbol,
15
+ SymbolAsyncIterator,
16
+ SymbolIterator,
14
17
} = primordials ;
15
18
16
19
const { AbortController, AbortSignal } = require ( 'internal/abort_controller' ) ;
@@ -39,6 +42,7 @@ const { isWritable, isNodeStream } = require('internal/streams/utils');
39
42
40
43
const kEmpty = Symbol ( 'kEmpty' ) ;
41
44
const kEof = Symbol ( 'kEof' ) ;
45
+ const kFlatMap = Symbol ( 'kFlatMap' ) ;
42
46
43
47
function compose ( stream , options ) {
44
48
if ( options != null ) {
@@ -92,11 +96,19 @@ function map(fn, options) {
92
96
93
97
highWaterMark += concurrency ;
94
98
99
+ const flatMap = options ?. [ kFlatMap ] != null ;
100
+
95
101
return async function * map ( ) {
96
102
const signal = AbortSignal . any ( [ options ?. signal ] . filter ( Boolean ) ) ;
97
103
const stream = this ;
98
104
const queue = [ ] ;
99
105
const signalOpt = { signal } ;
106
+ const baseIterator = ( async function * baseIterator ( ) {
107
+ for await ( const value of stream ) {
108
+ yield { result : fn ( value , signalOpt ) } ;
109
+ }
110
+ } ) ( ) ;
111
+ const iterators = new SafeSet ( [ baseIterator ] ) ;
100
112
101
113
let next ;
102
114
let resume ;
@@ -125,45 +137,54 @@ function map(fn, options) {
125
137
}
126
138
}
127
139
140
+ function addIterator ( result ) {
141
+ if ( result && ( result [ SymbolAsyncIterator ] || result [ SymbolIterator ] ) ) {
142
+ const iterator = result [ SymbolAsyncIterator ] ? result [ SymbolAsyncIterator ] ( ) : result [ SymbolIterator ] ( ) ;
143
+ iterators . add ( iterator ) ;
144
+ return kEmpty ;
145
+ }
146
+ return result ;
147
+ }
148
+
128
149
async function pump ( ) {
129
150
try {
130
- for await ( let val of stream ) {
131
- if ( done ) {
132
- return ;
133
- }
134
-
135
- if ( signal . aborted ) {
136
- throw new AbortError ( ) ;
137
- }
138
-
139
- try {
140
- val = fn ( val , signalOpt ) ;
141
-
142
- if ( val === kEmpty ) {
143
- continue ;
151
+ while ( iterators . size > 0 ) {
152
+ for ( const iterator of iterators ) {
153
+ if ( done ) {
154
+ return ;
144
155
}
145
156
146
- val = PromiseResolve ( val ) ;
147
- } catch ( err ) {
148
- val = PromiseReject ( err ) ;
149
- }
150
-
151
- cnt += 1 ;
157
+ if ( signal . aborted ) {
158
+ throw new AbortError ( ) ;
159
+ }
160
+ let val = PromisePrototypeThen ( PromiseResolve ( iterator . next ( ) ) , ( { value, done } ) => {
161
+ if ( done ) {
162
+ iterators . delete ( iterator ) ;
163
+ return kEmpty ;
164
+ }
165
+ return iterator === baseIterator ? value . result : value ;
166
+ } ) ;
152
167
153
- PromisePrototypeThen ( val , afterItemProcessed , onCatch ) ;
168
+ if ( flatMap && baseIterator === iterator ) {
169
+ val = PromisePrototypeThen ( val , addIterator ) ;
170
+ }
171
+ PromisePrototypeThen ( val , afterItemProcessed , onCatch ) ;
172
+ cnt += 1 ;
173
+ queue . push ( val ) ;
154
174
155
- queue . push ( val ) ;
156
- if ( next ) {
157
- next ( ) ;
158
- next = null ;
159
- }
175
+ if ( next ) {
176
+ next ( ) ;
177
+ next = null ;
178
+ }
160
179
161
- if ( ! done && ( queue . length >= highWaterMark || cnt >= concurrency ) ) {
162
- await new Promise ( ( resolve ) => {
163
- resume = resolve ;
164
- } ) ;
180
+ if ( ! done && ( queue . length >= highWaterMark || cnt >= concurrency ) ) {
181
+ await new Promise ( ( resolve ) => {
182
+ resume = resolve ;
183
+ } ) ;
184
+ }
165
185
}
166
186
}
187
+
167
188
queue . push ( kEof ) ;
168
189
} catch ( err ) {
169
190
const val = PromiseReject ( err ) ;
@@ -343,12 +364,10 @@ async function toArray(options) {
343
364
}
344
365
345
366
function flatMap ( fn , options ) {
346
- const values = map . call ( this , fn , options ) ;
347
- return async function * flatMap ( ) {
348
- for await ( const val of values ) {
349
- yield * val ;
350
- }
351
- } . call ( this ) ;
367
+ if ( options != null ) {
368
+ validateObject ( options , 'options' ) ;
369
+ }
370
+ return map . call ( this , fn , { ...options , [ kFlatMap ] : true } ) ;
352
371
}
353
372
354
373
function toIntegerOrInfinity ( number ) {
0 commit comments