@@ -8,6 +8,25 @@ const assert = require('assert');
8
8
const { once } = require ( 'events' ) ;
9
9
const { setTimeout } = require ( 'timers/promises' ) ;
10
10
11
+ function createDependentPromises ( n ) {
12
+ const promiseAndResolveArray = [ ] ;
13
+
14
+ for ( let i = 0 ; i < n ; i ++ ) {
15
+ let res ;
16
+ const promise = new Promise ( ( resolve ) => {
17
+ if ( i === 0 ) {
18
+ res = resolve ;
19
+ return ;
20
+ }
21
+ res = ( ) => promiseAndResolveArray [ i - 1 ] [ 0 ] . then ( resolve ) ;
22
+ } ) ;
23
+
24
+ promiseAndResolveArray . push ( [ promise , res ] ) ;
25
+ }
26
+
27
+ return promiseAndResolveArray ;
28
+ }
29
+
11
30
{
12
31
// Map works on synchronous streams with a synchronous mapper
13
32
const stream = Readable . from ( [ 1 , 2 , 3 , 4 , 5 ] ) . map ( ( x ) => x + x ) ;
@@ -173,15 +192,136 @@ const { setTimeout } = require('timers/promises');
173
192
} ) ( ) . then ( common . mustCall ( ) ) ;
174
193
}
175
194
195
+
196
+ {
197
+ // Simple pool-based concurrency
198
+ const finishOrder = [ ] ;
199
+
200
+ const promises = createDependentPromises ( 4 ) ;
201
+
202
+ const raw = Readable . from ( [ 2 , 0 , 1 , 3 ] ) ;
203
+ const stream = raw . map ( async ( item ) => {
204
+ const [ promise , resolve ] = promises [ item ] ;
205
+ resolve ( ) ;
206
+
207
+ await promise ;
208
+ finishOrder . push ( item ) ;
209
+ return item ;
210
+ } , { concurrency : 2 , pool : true } ) ;
211
+
212
+ ( async ( ) => {
213
+ await stream . toArray ( ) ;
214
+
215
+ assert . deepStrictEqual ( finishOrder , [ 0 , 1 , 2 , 3 ] ) ;
216
+ } ) ( ) . then ( common . mustCall ( ) , common . mustNotCall ( ) ) ;
217
+ }
218
+
219
+ {
220
+ // Pool-based concurrency with a lot of items and large concurrency
221
+ const finishOrder = [ ] ;
222
+
223
+ const promises = createDependentPromises ( 20 ) ;
224
+
225
+ const raw = Readable . from ( [ 11 , 1 , 0 , 3 , 4 , 2 , 5 , 7 , 8 , 9 , 6 , 10 , 12 , 13 , 18 , 15 , 16 , 17 , 14 , 19 ] ) ;
226
+ // Should be
227
+ // 11, 1, 0, 3, 4 | next: 0
228
+ // 11, 1, 3, 4, 2 | next: 1
229
+ // 11, 3, 4, 2, 5 | next: 2
230
+ // 11, 3, 4, 5, 7 | next: 3
231
+ // 11, 4, 5, 7, 8 | next: 4
232
+ // 11, 5, 7, 8, 9 | next: 5
233
+ // 11, 7, 8, 9, 6 | next: 6
234
+ // 11, 7, 8, 9, 10 | next: 7
235
+ // 11, 8, 9, 10, 12 | next: 8
236
+ // 11, 9, 10, 12, 13 | next: 9
237
+ // 11, 10, 12, 13, 18 | next: 10
238
+ // 11, 12, 13, 18, 15 | next: 11
239
+ // 12, 13, 18, 15, 16 | next: 12
240
+ // 13, 18, 15, 16, 17 | next: 13
241
+ // 18, 15, 16, 17, 14 | next: 14
242
+ // 18, 15, 16, 17, 19 | next: 15
243
+ // 18, 16, 17, 19 | next: 16
244
+ // 18, 17, 19 | next: 17
245
+ // 18, 19 | next: 18
246
+ // 19 | next: 19
247
+ //
248
+
249
+ const stream = raw . map ( async ( item ) => {
250
+ const [ promise , resolve ] = promises [ item ] ;
251
+ resolve ( ) ;
252
+
253
+ await promise ;
254
+ finishOrder . push ( item ) ;
255
+ return item ;
256
+ } , { concurrency : 5 , pool : true } ) ;
257
+
258
+ ( async ( ) => {
259
+ await stream . toArray ( ) ;
260
+
261
+ assert . deepStrictEqual ( finishOrder , [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 ] ) ;
262
+ } ) ( ) . then ( common . mustCall ( ) , common . mustNotCall ( ) ) ;
263
+ }
264
+
265
+ {
266
+ // Pool-based concurrency where there is a delay between the first and the next item in the pool
267
+ const finishOrder = [ ] ;
268
+
269
+ const raw = Readable . from ( ( async function * ( ) {
270
+ yield 200 ;
271
+
272
+ // Making sure the first item (200) finish before the next item (0) starts
273
+ // this is to make sure we don't wait for the pool to be filled before starting
274
+ await setTimeout ( 500 ) ;
275
+
276
+ yield 0 ;
277
+ yield 1 ;
278
+ } ) ( ) ) ;
279
+
280
+ let start ;
281
+ let delaysBetweenFirstAndNextPoolItem ;
282
+
283
+ const stream = raw
284
+ . map ( async ( item ) => {
285
+ await setTimeout ( item ) ;
286
+ finishOrder . push ( item ) ;
287
+
288
+ return item ;
289
+ } , { concurrency : 2 , pool : true } )
290
+ . map ( ( item ) => {
291
+ if ( item === 200 ) {
292
+ start = Date . now ( ) ;
293
+ }
294
+
295
+ if ( item === 0 ) {
296
+ delaysBetweenFirstAndNextPoolItem = Date . now ( ) - start ;
297
+ }
298
+
299
+ return item ;
300
+ } ) ;
301
+
302
+ ( async ( ) => {
303
+ await stream . toArray ( ) ;
304
+
305
+ assert . deepStrictEqual ( finishOrder , [ 200 , 0 , 1 ] ) ;
306
+ // More than 250ms because the first item waits for 200ms and the next item have delay of 500ms
307
+ assert . ok ( delaysBetweenFirstAndNextPoolItem > 250 , new Error ( `delay between first and next item in the pool should be more than 250ms but instead got ${ delaysBetweenFirstAndNextPoolItem } ` ) ) ;
308
+ } ) ( ) . then ( common . mustCall ( ) , common . mustNotCall ( ) ) ;
309
+ }
310
+
176
311
{
177
312
// Error cases
178
313
assert . throws ( ( ) => Readable . from ( [ 1 ] ) . map ( 1 ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
179
314
assert . throws ( ( ) => Readable . from ( [ 1 ] ) . map ( ( x ) => x , {
180
315
concurrency : 'Foo'
181
316
} ) , / E R R _ O U T _ O F _ R A N G E / ) ;
317
+ assert . throws ( ( ) => Readable . from ( [ 1 ] ) . map ( ( x ) => x , {
318
+ concurrency : 1 ,
319
+ pool : true
320
+ } ) , / E R R _ O U T _ O F _ R A N G E / ) ;
182
321
assert . throws ( ( ) => Readable . from ( [ 1 ] ) . map ( ( x ) => x , 1 ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
183
322
assert . throws ( ( ) => Readable . from ( [ 1 ] ) . map ( ( x ) => x , { signal : true } ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
184
323
}
324
+
185
325
{
186
326
// Test result is a Readable
187
327
const stream = Readable . from ( [ 1 , 2 , 3 , 4 , 5 ] ) . map ( ( x ) => x ) ;
0 commit comments