@handy-common-utils/promise-utils 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,18 @@
1
1
  # @handy-common-utils/promise-utils
2
2
 
3
- These Promise related utilities have 100% test coverage. The package is tiny because there is no dependency on any other package.
4
- Functions provided are `repeat`, `withRetry`, `inParallel`, `delayedResolve`, `delayedReject`, `timeoutResolve`, `timeoutReject`, `promiseState`, `synchronized`, etc.
3
+ These Promise-related utilities boast 100% test coverage, ensuring robust reliability.
4
+ The package, free of external dependencies, offers essential functions such as:
5
+
6
+ - `repeat`: Executes an operation repeatedly, very useful to collect all results through pagination.
7
+ - `withRetry`: Retries an operation until a specified condition is met.
8
+ - `withConcurrency`: Executes multiple operations with specified level of concurrency, and abort remaining operations when an error happens.
9
+ - `inParallel`: Executes multiple operations with specified level of concurrency, all operations are guaranteed to be executed regardless of any possible error.
10
+ - `delayedResolve`: Creates a Promise that resolves after a specified delay.
11
+ - `delayedReject`: Creates a Promise that rejects after a specified delay.
12
+ - `timeoutResolve`: Applies a timeout to a Promise and resolves with a specified result if the timeout occurs.
13
+ - `timeoutReject`: Applies a timeout to a Promise and rejects with a specified error/reason if the timeout occurs.
14
+ - `promiseState`: Retrieves the state of a Promise.
15
+ - `synchronized`: Provides mutual exclusion for concurrent operations using a lock mechanism, similar to `synchronized` in Java.
5
16
 
6
17
  [![Version](https://img.shields.io/npm/v/@handy-common-utils/promise-utils.svg)](https://npmjs.org/package/@handy-common-utils/promise-utils)
7
18
  [![Downloads/week](https://img.shields.io/npm/dw/@handy-common-utils/promise-utils.svg)](https://npmjs.org/package/@handy-common-utils/promise-utils)
@@ -59,16 +70,20 @@ const result2 = await withRetry(() => doSomething(), Array.from({length: 10}, (_
59
70
  const result3 = await withRetry(() => doSomething(), attempt => attempt <= 8 ? 1000 * Math.min(EXPONENTIAL_SEQUENCE[attempt - 1], 10) : undefined, err => err.statusCode === 429);
60
71
  statusCode === 429);
61
72
 
62
- // inParallel(...)
63
- const topicArns = topics.map(topic => topic.TopicArn!);
64
- await inParallel(5, topicArns, async topicArn => {
73
+ // Capture errors in the returned array
74
+ const attributesAndPossibleErrors = await PromiseUtils.inParallel(5, topicArns, async (topicArn) => {
65
75
  const topicAttributes = (await sns.getTopicAttributes({ TopicArn: topicArn }).promise()).Attributes!;
66
- const topicDetails = { ...topicAttributes, subscriptions: [] } as any;
67
- if (this.shouldInclude(topicArn)) {
68
- inventory.snsTopicsByArn.set(topicArn, topicDetails);
69
- }
76
+ return topicAttributes;
70
77
  });
71
78
 
79
+ // Abort on the first error
80
+ let results: Array<JobResult>;
81
+ try {
82
+ results = await PromiseUtils.withConcurrency(100, jobs, async (job) => processor.process(job));
83
+ } catch (error) {
84
+ // handle the error
85
+ }
86
+
72
87
  ```
73
88
 
74
89
  # API
@@ -130,7 +145,7 @@ PromiseUtils.withRetry(() => doSomething(), FIBONACCI_SEQUENCE.slice(0, 5).map(n
130
145
 
131
146
  #### delayedReject
132
147
 
133
- ▸ **delayedReject**<`T`, `R`\>(`ms`, `reason`): `Promise`<`T`\>
148
+ ▸ **delayedReject**\<`T`, `R`\>(`ms`, `reason`): `Promise`\<`T`\>
134
149
 
135
150
  See [delayedReject](#delayedreject) for full documentation.
136
151
 
@@ -146,17 +161,17 @@ See [delayedReject](#delayedreject) for full documentation.
146
161
  | Name | Type |
147
162
  | :------ | :------ |
148
163
  | `ms` | `number` |
149
- | `reason` | `R` \| `PromiseLike`<`R`\> \| () => `R` \| `PromiseLike`<`R`\> |
164
+ | `reason` | `R` \| `PromiseLike`\<`R`\> \| () => `R` \| `PromiseLike`\<`R`\> |
150
165
 
151
166
  ##### Returns
152
167
 
153
- `Promise`<`T`\>
168
+ `Promise`\<`T`\>
154
169
 
155
170
  ___
156
171
 
157
172
  #### delayedResolve
158
173
 
159
- ▸ **delayedResolve**<`T`\>(`ms`, `result?`): `Promise`<`T`\>
174
+ ▸ **delayedResolve**\<`T`\>(`ms`, `result?`): `Promise`\<`T`\>
160
175
 
161
176
  See [delayedResolve](#delayedresolve) for full documentation.
162
177
 
@@ -171,17 +186,17 @@ See [delayedResolve](#delayedresolve) for full documentation.
171
186
  | Name | Type |
172
187
  | :------ | :------ |
173
188
  | `ms` | `number` |
174
- | `result?` | `T` \| `PromiseLike`<`T`\> \| () => `T` \| `PromiseLike`<`T`\> |
189
+ | `result?` | `T` \| `PromiseLike`\<`T`\> \| () => `T` \| `PromiseLike`\<`T`\> |
175
190
 
176
191
  ##### Returns
177
192
 
178
- `Promise`<`T`\>
193
+ `Promise`\<`T`\>
179
194
 
180
195
  ___
181
196
 
182
197
  #### inParallel
183
198
 
184
- ▸ **inParallel**<`Data`, `Result`, `TError`\>(`parallelism`, `jobs`, `operation`, `options?`): `Promise`<(`Result` \| `TError`)[]\>
199
+ ▸ **inParallel**\<`Data`, `Result`, `TError`\>(`parallelism`, `jobs`, `operation`, `options?`): `Promise`\<(`Result` \| `TError`)[]\>
185
200
 
186
201
  See [inParallel](#inparallel) for full documentation.
187
202
 
@@ -198,20 +213,20 @@ See [inParallel](#inparallel) for full documentation.
198
213
  | Name | Type |
199
214
  | :------ | :------ |
200
215
  | `parallelism` | `number` |
201
- | `jobs` | `Iterable`<`Data`\> |
202
- | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`<`Result`\> |
216
+ | `jobs` | `Iterable`\<`Data`\> |
217
+ | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`\<`Result`\> |
203
218
  | `options?` | `Object` |
204
219
  | `options.abortOnError` | `boolean` |
205
220
 
206
221
  ##### Returns
207
222
 
208
- `Promise`<(`Result` \| `TError`)[]\>
223
+ `Promise`\<(`Result` \| `TError`)[]\>
209
224
 
210
225
  ___
211
226
 
212
227
  #### promiseState
213
228
 
214
- ▸ **promiseState**(`p`): `Promise`<[`PromiseState`](#enumspromisestatemd)\>
229
+ ▸ **promiseState**(`p`): `Promise`\<[`PromiseState`](#enumspromisestatemd)\>
215
230
 
216
231
  See [promiseState](#promisestate) for full documentation.
217
232
 
@@ -219,17 +234,17 @@ See [promiseState](#promisestate) for full documentation.
219
234
 
220
235
  | Name | Type |
221
236
  | :------ | :------ |
222
- | `p` | `Promise`<`any`\> |
237
+ | `p` | `Promise`\<`any`\> |
223
238
 
224
239
  ##### Returns
225
240
 
226
- `Promise`<[`PromiseState`](#enumspromisestatemd)\>
241
+ `Promise`\<[`PromiseState`](#enumspromisestatemd)\>
227
242
 
228
243
  ___
229
244
 
230
245
  #### repeat
231
246
 
232
- ▸ **repeat**<`Result`, `Param`, `Collection`\>(`operation`, `nextParameter`, `collect`, `initialCollection`, `initialParameter?`): `Promise`<`Collection`\>
247
+ ▸ **repeat**\<`Result`, `Param`, `Collection`\>(`operation`, `nextParameter`, `collect`, `initialCollection`, `initialParameter?`): `Promise`\<`Collection`\>
233
248
 
234
249
  See [repeat](#repeat) for full documentation.
235
250
 
@@ -245,21 +260,21 @@ See [repeat](#repeat) for full documentation.
245
260
 
246
261
  | Name | Type |
247
262
  | :------ | :------ |
248
- | `operation` | (`parameter`: `Partial`<`Param`\>) => `Promise`<`Result`\> |
249
- | `nextParameter` | (`response`: `Result`) => ``null`` \| `Partial`<`Param`\> \| `Promise`<`Partial`<`Param`\>\> |
263
+ | `operation` | (`parameter`: `Partial`\<`Param`\>) => `Promise`\<`Result`\> |
264
+ | `nextParameter` | (`response`: `Result`) => ``null`` \| `Partial`\<`Param`\> \| `Promise`\<`Partial`\<`Param`\>\> |
250
265
  | `collect` | (`collection`: `Collection`, `result`: `Result`) => `Collection` |
251
266
  | `initialCollection` | `Collection` |
252
- | `initialParameter` | `Partial`<`Param`\> |
267
+ | `initialParameter` | `Partial`\<`Param`\> |
253
268
 
254
269
  ##### Returns
255
270
 
256
- `Promise`<`Collection`\>
271
+ `Promise`\<`Collection`\>
257
272
 
258
273
  ___
259
274
 
260
275
  #### synchronised
261
276
 
262
- ▸ **synchronised**<`T`\>(`lock`, `operation`): `Promise`<`T`\>
277
+ ▸ **synchronised**\<`T`\>(`lock`, `operation`): `Promise`\<`T`\>
263
278
 
264
279
  See [synchronised](#synchronised) for full documentation.
265
280
 
@@ -273,18 +288,18 @@ See [synchronised](#synchronised) for full documentation.
273
288
 
274
289
  | Name | Type |
275
290
  | :------ | :------ |
276
- | `lock` | `unknown` |
277
- | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`<`T`\> |
291
+ | `lock` | `any` |
292
+ | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`\<`T`\> |
278
293
 
279
294
  ##### Returns
280
295
 
281
- `Promise`<`T`\>
296
+ `Promise`\<`T`\>
282
297
 
283
298
  ___
284
299
 
285
300
  #### synchronized
286
301
 
287
- ▸ **synchronized**<`T`\>(`lock`, `operation`): `Promise`<`T`\>
302
+ ▸ **synchronized**\<`T`\>(`lock`, `operation`): `Promise`\<`T`\>
288
303
 
289
304
  See [synchronized](#synchronized) for full documentation.
290
305
 
@@ -298,18 +313,18 @@ See [synchronized](#synchronized) for full documentation.
298
313
 
299
314
  | Name | Type |
300
315
  | :------ | :------ |
301
- | `lock` | `unknown` |
302
- | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`<`T`\> |
316
+ | `lock` | `any` |
317
+ | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`\<`T`\> |
303
318
 
304
319
  ##### Returns
305
320
 
306
- `Promise`<`T`\>
321
+ `Promise`\<`T`\>
307
322
 
308
323
  ___
309
324
 
310
325
  #### timeoutReject
311
326
 
312
- ▸ **timeoutReject**<`T`, `R`\>(`operation`, `ms`, `rejectReason`): `Promise`<`T`\>
327
+ ▸ **timeoutReject**\<`T`, `R`\>(`operation`, `ms`, `rejectReason`): `Promise`\<`T`\>
313
328
 
314
329
  See [timeoutReject](#timeoutreject) for full documentation.
315
330
 
@@ -324,19 +339,19 @@ See [timeoutReject](#timeoutreject) for full documentation.
324
339
 
325
340
  | Name | Type |
326
341
  | :------ | :------ |
327
- | `operation` | `Promise`<`T`\> \| () => `Promise`<`T`\> |
342
+ | `operation` | `Promise`\<`T`\> \| () => `Promise`\<`T`\> |
328
343
  | `ms` | `number` |
329
- | `rejectReason` | `R` \| `PromiseLike`<`R`\> \| () => `R` \| `PromiseLike`<`R`\> |
344
+ | `rejectReason` | `R` \| `PromiseLike`\<`R`\> \| () => `R` \| `PromiseLike`\<`R`\> |
330
345
 
331
346
  ##### Returns
332
347
 
333
- `Promise`<`T`\>
348
+ `Promise`\<`T`\>
334
349
 
335
350
  ___
336
351
 
337
352
  #### timeoutResolve
338
353
 
339
- ▸ **timeoutResolve**<`T`\>(`operation`, `ms`, `result?`): `Promise`<`T`\>
354
+ ▸ **timeoutResolve**\<`T`\>(`operation`, `ms`, `result?`): `Promise`\<`T`\>
340
355
 
341
356
  See [timeoutResolve](#timeoutresolve) for full documentation.
342
357
 
@@ -350,19 +365,46 @@ See [timeoutResolve](#timeoutresolve) for full documentation.
350
365
 
351
366
  | Name | Type |
352
367
  | :------ | :------ |
353
- | `operation` | `Promise`<`T`\> \| () => `Promise`<`T`\> |
368
+ | `operation` | `Promise`\<`T`\> \| () => `Promise`\<`T`\> |
354
369
  | `ms` | `number` |
355
- | `result?` | `T` \| `PromiseLike`<`T`\> \| () => `T` \| `PromiseLike`<`T`\> |
370
+ | `result?` | `T` \| `PromiseLike`\<`T`\> \| () => `T` \| `PromiseLike`\<`T`\> |
371
+
372
+ ##### Returns
373
+
374
+ `Promise`\<`T`\>
375
+
376
+ ___
377
+
378
+ #### withConcurrency
379
+
380
+ ▸ **withConcurrency**\<`Data`, `Result`\>(`concurrency`, `jobs`, `operation`): `Promise`\<`Result`[]\>
381
+
382
+ See [withConcurrency](#withconcurrency) for full documentation.
383
+
384
+ ##### Type parameters
385
+
386
+ | Name |
387
+ | :------ |
388
+ | `Data` |
389
+ | `Result` |
390
+
391
+ ##### Parameters
392
+
393
+ | Name | Type |
394
+ | :------ | :------ |
395
+ | `concurrency` | `number` |
396
+ | `jobs` | `Iterable`\<`Data`\> |
397
+ | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`\<`Result`\> |
356
398
 
357
399
  ##### Returns
358
400
 
359
- `Promise`<`T`\>
401
+ `Promise`\<`Result`[]\>
360
402
 
361
403
  ___
362
404
 
363
405
  #### withRetry
364
406
 
365
- ▸ **withRetry**<`Result`, `TError`\>(`operation`, `backoff`, `shouldRetry?`): `Promise`<`Result`\>
407
+ ▸ **withRetry**\<`Result`, `TError`\>(`operation`, `backoff`, `shouldRetry?`): `Promise`\<`Result`\>
366
408
 
367
409
  See [withRetry](#withretry) for full documentation.
368
410
 
@@ -377,13 +419,13 @@ See [withRetry](#withretry) for full documentation.
377
419
 
378
420
  | Name | Type |
379
421
  | :------ | :------ |
380
- | `operation` | (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `Promise`<`Result`\> |
422
+ | `operation` | (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `Promise`\<`Result`\> |
381
423
  | `backoff` | `number`[] \| (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `undefined` \| `number` |
382
424
  | `shouldRetry` | (`previousError`: `undefined` \| `TError`, `previousResult`: `undefined` \| `Result`, `attempt`: `number`) => `boolean` |
383
425
 
384
426
  ##### Returns
385
427
 
386
- `Promise`<`Result`\>
428
+ `Promise`\<`Result`\>
387
429
 
388
430
  ## Classes
389
431
 
@@ -402,9 +444,9 @@ See [withRetry](#withretry) for full documentation.
402
444
 
403
445
  ##### delayedReject
404
446
 
405
- ▸ `Static` **delayedReject**<`T`, `R`\>(`ms`, `reason`): `Promise`<`T`\>
447
+ ▸ `Static` **delayedReject**\<`T`, `R`\>(`ms`, `reason`): `Promise`\<`T`\>
406
448
 
407
- Create a Promise that rejects after number of milliseconds specified.
449
+ Creates a Promise that rejects after a specified number of milliseconds.
408
450
 
409
451
  ###### Type parameters
410
452
 
@@ -417,22 +459,22 @@ Create a Promise that rejects after number of milliseconds specified.
417
459
 
418
460
  | Name | Type | Description |
419
461
  | :------ | :------ | :------ |
420
- | `ms` | `number` | number of milliseconds after which the created Promise would reject |
421
- | `reason` | `R` \| `PromiseLike`<`R`\> \| () => `R` \| `PromiseLike`<`R`\> | the reason of the rejection for the Promise, or a function that supplies the reason. If the reason ends up to be a rejected Promise, then the outcome (could be fulfilled or rejected) of it will be the reject reason of the Promise returned. |
462
+ | `ms` | `number` | The number of milliseconds after which the created Promise will reject. |
463
+ | `reason` | `R` \| `PromiseLike`\<`R`\> \| () => `R` \| `PromiseLike`\<`R`\> | The reason for the rejection, or a function that supplies the reason. If the reason is a rejected Promise, the outcome of it will be the rejection reason of the returned Promise. |
422
464
 
423
465
  ###### Returns
424
466
 
425
- `Promise`<`T`\>
467
+ `Promise`\<`T`\>
426
468
 
427
- the new Promise created
469
+ A new Promise that rejects with the specified reason after the specified delay.
428
470
 
429
471
  ___
430
472
 
431
473
  ##### delayedResolve
432
474
 
433
- ▸ `Static` **delayedResolve**<`T`\>(`ms`, `result?`): `Promise`<`T`\>
475
+ ▸ `Static` **delayedResolve**\<`T`\>(`ms`, `result?`): `Promise`\<`T`\>
434
476
 
435
- Create a Promise that resolves after number of milliseconds specified
477
+ Creates a Promise that resolves after a specified number of milliseconds.
436
478
 
437
479
  ###### Type parameters
438
480
 
@@ -444,64 +486,67 @@ Create a Promise that resolves after number of milliseconds specified
444
486
 
445
487
  | Name | Type | Description |
446
488
  | :------ | :------ | :------ |
447
- | `ms` | `number` | number of milliseconds after which the created Promise would resolve |
448
- | `result?` | `T` \| `PromiseLike`<`T`\> \| () => `T` \| `PromiseLike`<`T`\> | the result to be resolved for the Promise, or a function that supplies the result. |
489
+ | `ms` | `number` | The number of milliseconds after which the created Promise will resolve. |
490
+ | `result?` | `T` \| `PromiseLike`\<`T`\> \| () => `T` \| `PromiseLike`\<`T`\> | The result to be resolved by the Promise, or a function that supplies the result. |
449
491
 
450
492
  ###### Returns
451
493
 
452
- `Promise`<`T`\>
494
+ `Promise`\<`T`\>
453
495
 
454
- the new Promise created
496
+ A new Promise that resolves with the specified result after the specified delay.
455
497
 
456
498
  ___
457
499
 
458
500
  ##### inParallel
459
501
 
460
- ▸ `Static` **inParallel**<`Data`, `Result`, `TError`\>(`parallelism`, `jobs`, `operation`, `options?`): `Promise`<(`Result` \| `TError`)[]\>
502
+ ▸ `Static` **inParallel**\<`Data`, `Result`, `TError`\>(`parallelism`, `jobs`, `operation`, `options?`): `Promise`\<(`Result` \| `TError`)[]\>
461
503
 
462
- Run multiple jobs/operations in parallel.
504
+ Executes multiple jobs/operations in parallel. By default, all operations are executed regardless of any failures.
505
+ In most cases, using [withConcurrency](#withconcurrency) might be more convenient.
463
506
 
464
- By default this function does not throw / reject with error when any of the job/operation fails.
465
- Operation errors are returned together with operation results in the same returned array.
466
- That also means this function only returns when all the jobs/operations settle (either resolve or reject).
507
+ By default, this function does not throw or reject an error when any job/operation fails.
508
+ Errors from operations are returned alongside results in the returned array.
509
+ This function only resolves when all jobs/operations are settled (either resolved or rejected).
467
510
 
468
- However, if options.abortOnError is true, this function throws / rejects with error when any of the job/operation fails.
469
- That also means, some of the jobs/operations may not get the chance to be executed if one of them fails.
511
+ If `options.abortOnError` is set to true, this function throws (or rejects with) an error immediately when any job/operation fails.
512
+ In this mode, some jobs/operations may not be executed if one fails.
470
513
 
471
514
  ###### Type parameters
472
515
 
473
516
  | Name | Type | Description |
474
517
  | :------ | :------ | :------ |
475
- | `Data` | `Data` | Type of the job data, usually it would be an Array |
476
- | `Result` | `Result` | Type of the return value of the operation function |
477
- | `TError` | `Result` | - |
518
+ | `Data` | `Data` | The type of the job data, typically an Array. |
519
+ | `Result` | `Result` | The type of the return value from the operation function. |
520
+ | `TError` | `Result` | The type for the error that could be thrown from the operation function, defaults to `Result`. |
478
521
 
479
522
  ###### Parameters
480
523
 
481
524
  | Name | Type | Description |
482
525
  | :------ | :------ | :------ |
483
- | `parallelism` | `number` | how many jobs/operations can be running at the same time |
484
- | `jobs` | `Iterable`<`Data`\> | job data which will be the input to operation function. This function is safe when there are infinite unknown number of elements in the job data. |
485
- | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`<`Result`\> | the function that turns job data into result asynchronously |
486
- | `options?` | `Object` | Options for controlling the behavior of this function. |
487
- | `options.abortOnError` | `boolean` | - |
526
+ | `parallelism` | `number` | The number of jobs/operations to run concurrently. |
527
+ | `jobs` | `Iterable`\<`Data`\> | The job data to be processed. This function can safely handle an infinite or unknown number of elements. |
528
+ | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`\<`Result`\> | The function that processes job data asynchronously. |
529
+ | `options?` | `Object` | Options to control the function's behavior. |
530
+ | `options.abortOnError` | `boolean` | If true, the function aborts and throws an error on the first failed operation. |
488
531
 
489
532
  ###### Returns
490
533
 
491
- `Promise`<(`Result` \| `TError`)[]\>
534
+ `Promise`\<(`Result` \| `TError`)[]\>
492
535
 
493
- Promise of void if the operation function does not return a value,
494
- or promise of an array containing outcomes from the operation function.
495
- In the returned array containing outcomes, each element is either the fulfilled result, or the rejected error/reason.
536
+ A promise that resolves to an array containing the results of the operations.
537
+ Each element is either a fulfilled result or a rejected error/reason.
538
+ The results or errors in the returned array are in the same order as the corresponding elements in the jobs array.
496
539
 
497
540
  **`Example`**
498
541
 
499
542
  ```ts
500
- const attributesAndPossibleErrors = await PromiseUtils.inParallel(5, topicArns, async (topicArn) => {
543
+ // Capture errors in the returned array
544
+ const attributesAndPossibleErrors: Array<JobResult|JobError> = await PromiseUtils.inParallel(5, topicArns, async (topicArn) => {
501
545
  const topicAttributes = (await sns.getTopicAttributes({ TopicArn: topicArn }).promise()).Attributes!;
502
546
  return topicAttributes;
503
547
  });
504
548
 
549
+ // Abort on the first error
505
550
  let results: Array<JobResult>;
506
551
  try {
507
552
  results = await PromiseUtils.inParallel(100, jobs, async (job) => processor.process(job), { abortOnError: true });
@@ -514,55 +559,55 @@ ___
514
559
 
515
560
  ##### promiseState
516
561
 
517
- ▸ `Static` **promiseState**(`p`): `Promise`<[`PromiseState`](#enumspromisestatemd)\>
562
+ ▸ `Static` **promiseState**(`p`): `Promise`\<[`PromiseState`](#enumspromisestatemd)\>
518
563
 
519
- Get the state of the Promise.
520
- Please note that the returned value is a Promise, although it resolves immediately.
564
+ Retrieves the state of the specified Promise.
565
+ Note: The returned value is a Promise that resolves immediately.
521
566
 
522
567
  ###### Parameters
523
568
 
524
569
  | Name | Type | Description |
525
570
  | :------ | :------ | :------ |
526
- | `p` | `Promise`<`any`\> | the Promise for which we would like to know its state |
571
+ | `p` | `Promise`\<`any`\> | The Promise whose state is to be determined. |
527
572
 
528
573
  ###### Returns
529
574
 
530
- `Promise`<[`PromiseState`](#enumspromisestatemd)\>
575
+ `Promise`\<[`PromiseState`](#enumspromisestatemd)\>
531
576
 
532
- A Promise that resolves immediately containing the state of the input Promise
577
+ A Promise that resolves immediately with the state of the input Promise.
533
578
 
534
579
  ___
535
580
 
536
581
  ##### repeat
537
582
 
538
- ▸ `Static` **repeat**<`Result`, `Param`, `Collection`\>(`operation`, `nextParameter`, `collect`, `initialCollection`, `initialParameter?`): `Promise`<`Collection`\>
583
+ ▸ `Static` **repeat**\<`Result`, `Param`, `Collection`\>(`operation`, `nextParameter`, `collect`, `initialCollection`, `initialParameter?`): `Promise`\<`Collection`\>
539
584
 
540
- Do an operation repeatedly and collect all the results.
541
- This function is useful for client side pagination.
585
+ Executes an operation repeatedly and collects all the results.
586
+ This function is very useful for many scenarios, such like client-side pagination.
542
587
 
543
588
  ###### Type parameters
544
589
 
545
590
  | Name | Description |
546
591
  | :------ | :------ |
547
- | `Result` | type of the operation result |
548
- | `Param` | type of the input to the operation, normally the input is a paging parameter |
549
- | `Collection` | type of the returned value of this function |
592
+ | `Result` | The type of the operation result. |
593
+ | `Param` | The type of the input to the operation, typically a paging parameter. |
594
+ | `Collection` | The type of the collection returned by this function. |
550
595
 
551
596
  ###### Parameters
552
597
 
553
598
  | Name | Type | Description |
554
599
  | :------ | :------ | :------ |
555
- | `operation` | (`parameter`: `Partial`<`Param`\>) => `Promise`<`Result`\> | a function that takes paging parameter as input and outputs a result, normally the operation supports paging |
556
- | `nextParameter` | (`response`: `Result`) => ``null`` \| `Partial`<`Param`\> \| `Promise`<`Partial`<`Param`\>\> | The function for calculating next parameter from the operation result. Normally the parameter controls paging, This function should return null when next invocation of the operation function is not desired. If next invocation is desired, the return value of this function can be a Promise or not a Promise. |
557
- | `collect` | (`collection`: `Collection`, `result`: `Result`) => `Collection` | the function for merging operation result into the collection |
558
- | `initialCollection` | `Collection` | initial collection which would be the first argument passed into the first invocation of the collect function |
559
- | `initialParameter` | `Partial`<`Param`\> | the parameter for the first operation |
600
+ | `operation` | (`parameter`: `Partial`\<`Param`\>) => `Promise`\<`Result`\> | A function that takes a parameter as input and returns a result. Typically, the parameter has optional fields to control paging. |
601
+ | `nextParameter` | (`response`: `Result`) => ``null`` \| `Partial`\<`Param`\> \| `Promise`\<`Partial`\<`Param`\>\> | A function for calculating the next parameter from the operation result. Normally, this parameter controls paging. This function should return null when no further invocation of the operation function is desired. If further invocation is desired, the return value of this function can be a Promise or a non-Promise value. |
602
+ | `collect` | (`collection`: `Collection`, `result`: `Result`) => `Collection` | A function for merging the operation result into the collection. |
603
+ | `initialCollection` | `Collection` | The initial collection, which will be the first argument passed to the first invocation of the collect function. |
604
+ | `initialParameter` | `Partial`\<`Param`\> | The parameter for the first operation. |
560
605
 
561
606
  ###### Returns
562
607
 
563
- `Promise`<`Collection`\>
608
+ `Promise`\<`Collection`\>
564
609
 
565
- Promise of collection of all the results returned by the operation function
610
+ A promise that resolves to a collection of all the results returned by the operation function.
566
611
 
567
612
  **`Example`**
568
613
 
@@ -579,7 +624,7 @@ ___
579
624
 
580
625
  ##### synchronised
581
626
 
582
- ▸ `Static` **synchronised**<`T`\>(`lock`, `operation`): `Promise`<`T`\>
627
+ ▸ `Static` **synchronised**\<`T`\>(`lock`, `operation`): `Promise`\<`T`\>
583
628
 
584
629
  This is just another spelling of [synchronized](#synchronized).
585
630
 
@@ -593,26 +638,27 @@ This is just another spelling of [synchronized](#synchronized).
593
638
 
594
639
  | Name | Type | Description |
595
640
  | :------ | :------ | :------ |
596
- | `lock` | `unknown` | the object (could be a string, a number, or `this` in a class) that is used to apply the lock |
597
- | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`<`T`\> | function for doing the computation and returning a Promise |
641
+ | `lock` | `any` | The object (such as a string, a number, or `this` in a class) used to identify the lock. |
642
+ | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`\<`T`\> | The function that performs the computation and returns a Promise. |
598
643
 
599
644
  ###### Returns
600
645
 
601
- `Promise`<`T`\>
646
+ `Promise`\<`T`\>
602
647
 
603
- the result of the operation function
648
+ The result of the operation function.
604
649
 
605
650
  ___
606
651
 
607
652
  ##### synchronized
608
653
 
609
- ▸ `Static` **synchronized**<`T`\>(`lock`, `operation`): `Promise`<`T`\>
654
+ ▸ `Static` **synchronized**\<`T`\>(`lock`, `operation`): `Promise`\<`T`\>
610
655
 
611
- Equivalent of `synchronized` in Java.
612
- In any situation there's no concurrent execution of any operation function associated with the same lock.
613
- The operation function has access to the state (when `synchronized` is called), settledState (when the operation function is called),
614
- and result (could be the fulfilled result or the rejected reason) of the previous operation.
615
- In case there is no previous invocation, state, settledState and result would all be undefined.
656
+ Provides mutual exclusion similar to `synchronized` in Java.
657
+ Ensures no concurrent execution of any operation function associated with the same lock.
658
+ The operation function has access to the state (when `synchronized` is called),
659
+ settledState (when the operation function is called),
660
+ and result (either the fulfilled result or the rejected reason) of the previous operation.
661
+ If there is no previous invocation, state, settledState, and result will all be undefined.
616
662
 
617
663
  ###### Type parameters
618
664
 
@@ -624,26 +670,26 @@ In case there is no previous invocation, state, settledState and result would al
624
670
 
625
671
  | Name | Type | Description |
626
672
  | :------ | :------ | :------ |
627
- | `lock` | `unknown` | the object (could be a string, a number, or `this` in a class) that is used to apply the lock |
628
- | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`<`T`\> | function for doing the computation and returning a Promise |
673
+ | `lock` | `any` | The object (such as a string, a number, or `this` in a class) used to identify the lock. |
674
+ | `operation` | (`previousState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousSettledState`: `undefined` \| [`PromiseState`](#enumspromisestatemd), `previousResult`: `any`) => `Promise`\<`T`\> | The function that performs the computation and returns a Promise. |
629
675
 
630
676
  ###### Returns
631
677
 
632
- `Promise`<`T`\>
678
+ `Promise`\<`T`\>
633
679
 
634
- the result of the operation function
680
+ The result of the operation function.
635
681
 
636
682
  ___
637
683
 
638
684
  ##### timeoutReject
639
685
 
640
- ▸ `Static` **timeoutReject**<`T`, `R`\>(`operation`, `ms`, `rejectReason`): `Promise`<`T`\>
686
+ ▸ `Static` **timeoutReject**\<`T`, `R`\>(`operation`, `ms`, `rejectReason`): `Promise`\<`T`\>
641
687
 
642
688
  Applies a timeout to a Promise or a function that returns a Promise.
643
- If the timeout occurs, rejects with the specified reason.
644
- If the timeout doesn't occur, the resolved result or rejection reason of the original Promise will be the outcome of the Promise returned from this function.
645
- If the 'reason' parameter is a function and timeout doesn't occur, the function won't be called.
646
- The rejection of the 'operation' parameter is not handled by this function, you may want to handle it outside of this function to avoid warnings like "(node:4330) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously".
689
+ If the timeout occurs, the returned Promise rejects with the specified reason.
690
+ If the timeout does not occur, the returned Promise resolves or rejects based on the outcome of the original Promise.
691
+ If the `rejectReason` parameter is a function and the timeout does not occur, the function will not be called.
692
+ Note: The rejection of the `operation` parameter is not handled by this function. You may want to handle it outside this function to avoid warnings like "(node:4330) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously."
647
693
 
648
694
  ###### Type parameters
649
695
 
@@ -656,13 +702,13 @@ The rejection of the 'operation' parameter is not handled by this function, you
656
702
 
657
703
  | Name | Type | Description |
658
704
  | :------ | :------ | :------ |
659
- | `operation` | `Promise`<`T`\> \| () => `Promise`<`T`\> | The original Promise or a function that returns a Promise for which the timeout will be applied. |
705
+ | `operation` | `Promise`\<`T`\> \| () => `Promise`\<`T`\> | The original Promise or a function that returns a Promise to which the timeout will be applied. |
660
706
  | `ms` | `number` | The number of milliseconds for the timeout. |
661
- | `rejectReason` | `R` \| `PromiseLike`<`R`\> \| () => `R` \| `PromiseLike`<`R`\> | The reason to reject with if the timeout occurs, or a function that supplies the reason. |
707
+ | `rejectReason` | `R` \| `PromiseLike`\<`R`\> \| () => `R` \| `PromiseLike`\<`R`\> | The reason to reject with if the timeout occurs, or a function that supplies the reason. |
662
708
 
663
709
  ###### Returns
664
710
 
665
- `Promise`<`T`\>
711
+ `Promise`\<`T`\>
666
712
 
667
713
  A new Promise that rejects with the specified reason if the timeout occurs.
668
714
 
@@ -670,13 +716,14 @@ ___
670
716
 
671
717
  ##### timeoutResolve
672
718
 
673
- ▸ `Static` **timeoutResolve**<`T`\>(`operation`, `ms`, `result?`): `Promise`<`T`\>
719
+ ▸ `Static` **timeoutResolve**\<`T`\>(`operation`, `ms`, `result?`): `Promise`\<`T`\>
674
720
 
675
721
  Applies a timeout to a Promise or a function that returns a Promise.
676
- If the timeout occurs, resolves to the specified result.
677
- If the timeout doesn't occur, the resolved result or rejection reason of the original Promise will be the outcome of the Promise returned from this function.
678
- If the 'result' parameter is a function and timeout doesn't occur, the function won't be called.
679
- The rejection of the 'operation' parameter is not handled by this function, you may want to handle it outside of this function to avoid warnings like "(node:4330) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously".
722
+ If the timeout occurs, the returned Promise resolves to the specified result.
723
+ If the timeout does not occur, the returned Promise resolves or rejects based on the outcome of the original Promise.
724
+ If the `result` parameter is a function and the timeout does not occur, the function will not be called.
725
+ Note: The rejection of the `operation` parameter is not handled by this function.
726
+ You may want to handle it outside this function to avoid warnings like "(node:4330) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously."
680
727
 
681
728
  ###### Type parameters
682
729
 
@@ -688,44 +735,88 @@ The rejection of the 'operation' parameter is not handled by this function, you
688
735
 
689
736
  | Name | Type | Description |
690
737
  | :------ | :------ | :------ |
691
- | `operation` | `Promise`<`T`\> \| () => `Promise`<`T`\> | The original Promise or a function that returns a Promise for which the timeout will be applied. |
738
+ | `operation` | `Promise`\<`T`\> \| () => `Promise`\<`T`\> | The original Promise or a function that returns a Promise to which the timeout will be applied. |
692
739
  | `ms` | `number` | The number of milliseconds for the timeout. |
693
- | `result?` | `T` \| `PromiseLike`<`T`\> \| () => `T` \| `PromiseLike`<`T`\> | The result to be resolved with if the timeout occurs, or a function that supplies the result. |
740
+ | `result?` | `T` \| `PromiseLike`\<`T`\> \| () => `T` \| `PromiseLike`\<`T`\> | The result to resolve with if the timeout occurs, or a function that supplies the result. |
694
741
 
695
742
  ###### Returns
696
743
 
697
- `Promise`<`T`\>
744
+ `Promise`\<`T`\>
698
745
 
699
746
  A new Promise that resolves to the specified result if the timeout occurs.
700
747
 
701
748
  ___
702
749
 
750
+ ##### withConcurrency
751
+
752
+ ▸ `Static` **withConcurrency**\<`Data`, `Result`\>(`concurrency`, `jobs`, `operation`): `Promise`\<`Result`[]\>
753
+
754
+ Executes multiple jobs/operations with a specified level of concurrency.
755
+
756
+ Unlike `inParallel(...)`, this function may throw or reject an error when a job/operation fails.
757
+ When an error is re-thrown, remaining operations will not be executed.
758
+ If you want all the operations to always be executed, use [inParallel](#inparallel) instead.
759
+
760
+ ###### Type parameters
761
+
762
+ | Name | Description |
763
+ | :------ | :------ |
764
+ | `Data` | The type of the job data, typically an Array. |
765
+ | `Result` | The type of the return value from the operation function. |
766
+
767
+ ###### Parameters
768
+
769
+ | Name | Type | Description |
770
+ | :------ | :------ | :------ |
771
+ | `concurrency` | `number` | The number of jobs/operations to run concurrently. |
772
+ | `jobs` | `Iterable`\<`Data`\> | The job data to be processed. This function can handle an infinite or unknown number of elements safely. |
773
+ | `operation` | (`job`: `Data`, `index`: `number`) => `Promise`\<`Result`\> | The function that processes job data asynchronously. |
774
+
775
+ ###### Returns
776
+
777
+ `Promise`\<`Result`[]\>
778
+
779
+ A promise that resolves to an array containing the results from the operation function.
780
+ The results in the returned array are in the same order as the corresponding elements in the jobs array.
781
+
782
+ **`Example`**
783
+
784
+ ```ts
785
+ // At any time, there would be no more than 5 concurrency API calls. Error would be re-thrown immediately when it occurs.
786
+ const attributes = await PromiseUtils.withConcurrency(5, topicArns, async (topicArn) => {
787
+ const topicAttributes = (await sns.getTopicAttributes({ TopicArn: topicArn }).promise()).Attributes!;
788
+ return topicAttributes;
789
+ });
790
+ ```
791
+
792
+ ___
793
+
703
794
  ##### withRetry
704
795
 
705
- ▸ `Static` **withRetry**<`Result`, `TError`\>(`operation`, `backoff`, `shouldRetry?`): `Promise`<`Result`\>
796
+ ▸ `Static` **withRetry**\<`Result`, `TError`\>(`operation`, `backoff`, `shouldRetry?`): `Promise`\<`Result`\>
706
797
 
707
- Do an operation repeatedly until a criteria is met.
798
+ Repeatedly performs an operation until a specified criteria is met.
708
799
 
709
800
  ###### Type parameters
710
801
 
711
802
  | Name | Type | Description |
712
803
  | :------ | :------ | :------ |
713
- | `Result` | `Result` | type of the operation result |
714
- | `TError` | `any` | type of the possible error that could be generated by the operation |
804
+ | `Result` | `Result` | Type of the operation result. |
805
+ | `TError` | `any` | Type of the possible error that could be generated by the operation. |
715
806
 
716
807
  ###### Parameters
717
808
 
718
809
  | Name | Type | Description |
719
810
  | :------ | :------ | :------ |
720
- | `operation` | (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `Promise`<`Result`\> | a function that outputs a Promise result, normally the operation does not use its arguments |
721
- | `backoff` | `number`[] \| (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `undefined` \| `number` | Array of retry backoff periods (unit: milliseconds) or function for calculating them. If retry is desired, before making next call to the operation the desired backoff period would be waited. If the array runs out of elements or the function returns `undefined` or either the array or the function returns a negative number, there would be no further call to the operation. The `attempt` argument passed into backoff function starts from 1 because the function is called right after the first attempt and before the first retry. |
722
- | `shouldRetry` | (`previousError`: `undefined` \| `TError`, `previousResult`: `undefined` \| `Result`, `attempt`: `number`) => `boolean` | Predicate function for deciding whether another call to the operation should happen. If this argument is not defined, retry would happen whenever the operation rejects with an error. `shouldRetry` would be evaluated before `backoff`. The `attempt` argument passed into shouldRetry function starts from 1. |
811
+ | `operation` | (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `Promise`\<`Result`\> | A function that outputs a Promise result. Typically, the operation does not use its arguments. |
812
+ | `backoff` | `number`[] \| (`attempt`: `number`, `previousResult`: `undefined` \| `Result`, `previousError`: `undefined` \| `TError`) => `undefined` \| `number` | An array of retry backoff periods (in milliseconds) or a function for calculating them. If retry is desired, the specified backoff period is waited before the next call to the operation. If the array runs out of elements or the function returns `undefined` or a negative number, no further calls to the operation will be made. The `attempt` argument passed to the backoff function starts from 1, as it is called immediately after the first attempt and before the first retry. |
813
+ | `shouldRetry` | (`previousError`: `undefined` \| `TError`, `previousResult`: `undefined` \| `Result`, `attempt`: `number`) => `boolean` | A predicate function for deciding whether another call to the operation should occur. If this argument is not defined, a retry will occur whenever the operation rejects with an error. The `shouldRetry` function is evaluated before the `backoff`. The `attempt` argument passed to the shouldRetry function starts from 1. |
723
814
 
724
815
  ###### Returns
725
816
 
726
- `Promise`<`Result`\>
817
+ `Promise`\<`Result`\>
727
818
 
728
- Promise of the operation result potentially with retries already applied
819
+ A promise of the operation result, potentially with retries applied.
729
820
 
730
821
  **`Example`**
731
822