@shutterstock/p-map-iterable 1.1.0 → 1.1.2

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.
Files changed (42) hide show
  1. package/README.md +105 -14
  2. package/dist/src/iterable-mapper.d.ts +173 -30
  3. package/dist/src/iterable-mapper.js +143 -25
  4. package/dist/src/iterable-mapper.js.map +1 -1
  5. package/dist/src/iterable-queue-mapper-simple.d.ts +44 -29
  6. package/dist/src/iterable-queue-mapper-simple.js +38 -21
  7. package/dist/src/iterable-queue-mapper-simple.js.map +1 -1
  8. package/dist/src/iterable-queue-mapper.d.ts +38 -58
  9. package/dist/src/iterable-queue-mapper.js +33 -33
  10. package/dist/src/iterable-queue-mapper.js.map +1 -1
  11. package/package.json +3 -3
  12. package/dist/src/blocking-queue.d.ts.map +0 -1
  13. package/dist/src/blocking-queue.test.d.ts +0 -2
  14. package/dist/src/blocking-queue.test.d.ts.map +0 -1
  15. package/dist/src/blocking-queue.test.js +0 -174
  16. package/dist/src/blocking-queue.test.js.map +0 -1
  17. package/dist/src/index.d.ts.map +0 -1
  18. package/dist/src/iterable-mapper.d.ts.map +0 -1
  19. package/dist/src/iterable-mapper.test.d.ts +0 -2
  20. package/dist/src/iterable-mapper.test.d.ts.map +0 -1
  21. package/dist/src/iterable-mapper.test.js +0 -870
  22. package/dist/src/iterable-mapper.test.js.map +0 -1
  23. package/dist/src/iterable-queue-mapper-simple.d.ts.map +0 -1
  24. package/dist/src/iterable-queue-mapper-simple.test.d.ts +0 -2
  25. package/dist/src/iterable-queue-mapper-simple.test.d.ts.map +0 -1
  26. package/dist/src/iterable-queue-mapper-simple.test.js +0 -102
  27. package/dist/src/iterable-queue-mapper-simple.test.js.map +0 -1
  28. package/dist/src/iterable-queue-mapper.d.ts.map +0 -1
  29. package/dist/src/iterable-queue-mapper.test.d.ts +0 -2
  30. package/dist/src/iterable-queue-mapper.test.d.ts.map +0 -1
  31. package/dist/src/iterable-queue-mapper.test.js +0 -117
  32. package/dist/src/iterable-queue-mapper.test.js.map +0 -1
  33. package/dist/src/iterable-queue.d.ts.map +0 -1
  34. package/dist/src/iterable-queue.test.d.ts +0 -2
  35. package/dist/src/iterable-queue.test.d.ts.map +0 -1
  36. package/dist/src/iterable-queue.test.js +0 -143
  37. package/dist/src/iterable-queue.test.js.map +0 -1
  38. package/dist/src/queue.d.ts.map +0 -1
  39. package/dist/src/queue.test.d.ts +0 -2
  40. package/dist/src/queue.test.d.ts.map +0 -1
  41. package/dist/src/queue.test.js +0 -75
  42. package/dist/src/queue.test.js.map +0 -1
@@ -1,29 +1,53 @@
1
- import { Mapper } from './iterable-mapper';
1
+ import { IterableMapperOptions, Mapper } from './iterable-mapper';
2
2
  type Errors<T> = {
3
3
  item: T;
4
4
  error: string | {
5
5
  [key: string]: any;
6
6
  } | Error;
7
7
  }[];
8
+ /**
9
+ * Options for IterableQueueMapperSimple
10
+ */
11
+ export type IterableQueueMapperSimpleOptions = Pick<IterableMapperOptions, 'concurrency'>;
8
12
  /**
9
13
  * Accepts queue items via `enqueue` and calls the `mapper` on them
10
- * with specified concurrency, storing the
11
- * `mapper` result in a queue of specified max size, before
12
- * being iterated / read by the caller. The `enqueue` method will block if
13
- * the queue is full, until an item is read.
14
+ * with specified `concurrency`, discards the results, and accumulates
15
+ * exceptions in the `errors` property. When empty, `await enqueue()`
16
+ * will return immediately, but when `concurrency` items are in progress,
17
+ * `await enqueue()` will block until a slot is available to accept the item.
14
18
  *
15
19
  * @remarks
16
20
  *
17
- * Note: the name is somewhat of a misnomer as this wraps `IterableQueueMapper`
18
- * but is not itself an `Iterable`.
21
+ * ### Typical Use Case
22
+ * - Pushing items to an async I/O destination
23
+ * - In the simple sequential (`concurrency: 1`) case, allows 1 item to be flushed async while caller prepares next item
24
+ * - Results of the flushed items are not needed in a subsequent step (if they are, use `IterableQueueMapper`)
25
+ *
26
+ * ### Error Handling
27
+ * The mapper should ideally handle all errors internally to enable error handling
28
+ * closest to where they occur. However, if errors do escape the mapper:
29
+ * - Processing continues despite errors
30
+ * - All errors are collected in the `errors` property
31
+ * - Errors can be checked/handled during processing via the `errors` property
32
+ *
33
+ * Key Differences from `IterableQueueMapper`:
34
+ * - `maxUnread` defaults to equal `concurrency` (simplifying queue management)
35
+ * - Results are automatically iterated and discarded (all work should happen in mapper)
36
+ * - Errors are collected rather than thrown (available via errors property)
19
37
  *
20
- * Accepts items for mapping in the background, discards the results,
21
- * but accumulates exceptions in the `errors` property.
38
+ * ### Usage
39
+ * - Items are added to the queue via the `await enqueue()` method
40
+ * - Check `errors` property to see if any errors occurred, stop if desired
41
+ * - IMPORTANT: `await enqueue()` method will block until a slot is available, if queue is full
42
+ * - IMPORTANT: Always `await onIdle()` to ensure all items are processed
22
43
  *
23
- * Allows up to `concurrency` mappers to be in progress before
24
- * `enqueue` will block until a mapper completes.
44
+ * Note: the name is somewhat of a misnomer as this wraps `IterableQueueMapper`
45
+ * but is not itself an `Iterable`.
25
46
  *
26
47
  * @category Enqueue Input
48
+ *
49
+ * @see {@link IterableQueueMapper} for related class with more configuration options
50
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
27
51
  */
28
52
  export declare class IterableQueueMapperSimple<Element> {
29
53
  private readonly _writer;
@@ -32,26 +56,17 @@ export declare class IterableQueueMapperSimple<Element> {
32
56
  private readonly _mapper;
33
57
  private _isIdle;
34
58
  /**
35
- * Create a new `IterableQueueMapperSimple`
59
+ * Create a new `IterableQueueMapperSimple`, which uses `IterableQueueMapper` underneath, but
60
+ * automatically iterates and discards results as they complete.
36
61
  *
37
- * @param mapper Function which is called for every item in `input`.
38
- * Expected to return a `Promise` or value.
39
- *
40
- * The `mapper` *should* handle all errors and not allow an error to be thrown
41
- * out of the `mapper` function as this enables the best handling of errors
42
- * closest to the time that they occur.
43
- *
44
- * If the `mapper` function does allow an error to be thrown then the
45
- * errors will be accumulated in the `errors` property.
62
+ * @param mapper Function called for every enqueued item. Returns a `Promise` or value.
46
63
  * @param options IterableQueueMapperSimple options
64
+ *
65
+ * @see {@link IterableQueueMapperSimple} for full class documentation
66
+ * @see {@link IterableQueueMapper} for related class with more configuration options
67
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
47
68
  */
48
- constructor(mapper: Mapper<Element, void>, options?: {
49
- /**
50
- * Number of items to accept for mapping before requiring the caller to wait for one to complete.
51
- * @default 4
52
- */
53
- concurrency?: number;
54
- });
69
+ constructor(mapper: Mapper<Element, void>, options?: IterableQueueMapperSimpleOptions);
55
70
  private discardResults;
56
71
  private worker;
57
72
  /**
@@ -70,7 +85,7 @@ export declare class IterableQueueMapperSimple<Element> {
70
85
  /**
71
86
  * Accept a request for sending in the background if a concurrency slot is available.
72
87
  * Else, do not return until a concurrency slot is freed up.
73
- * This provides concurrency background writes with back pressure to prevent
88
+ * This provides concurrency background writes with backpressure to prevent
74
89
  * the caller from getting too far ahead.
75
90
  *
76
91
  * MUST await `onIdle` for background `mappers`s to finish
@@ -5,38 +5,55 @@ const iterable_queue_mapper_1 = require("./iterable-queue-mapper");
5
5
  const NoResult = Symbol('noresult');
6
6
  /**
7
7
  * Accepts queue items via `enqueue` and calls the `mapper` on them
8
- * with specified concurrency, storing the
9
- * `mapper` result in a queue of specified max size, before
10
- * being iterated / read by the caller. The `enqueue` method will block if
11
- * the queue is full, until an item is read.
8
+ * with specified `concurrency`, discards the results, and accumulates
9
+ * exceptions in the `errors` property. When empty, `await enqueue()`
10
+ * will return immediately, but when `concurrency` items are in progress,
11
+ * `await enqueue()` will block until a slot is available to accept the item.
12
12
  *
13
13
  * @remarks
14
14
  *
15
- * Note: the name is somewhat of a misnomer as this wraps `IterableQueueMapper`
16
- * but is not itself an `Iterable`.
15
+ * ### Typical Use Case
16
+ * - Pushing items to an async I/O destination
17
+ * - In the simple sequential (`concurrency: 1`) case, allows 1 item to be flushed async while caller prepares next item
18
+ * - Results of the flushed items are not needed in a subsequent step (if they are, use `IterableQueueMapper`)
19
+ *
20
+ * ### Error Handling
21
+ * The mapper should ideally handle all errors internally to enable error handling
22
+ * closest to where they occur. However, if errors do escape the mapper:
23
+ * - Processing continues despite errors
24
+ * - All errors are collected in the `errors` property
25
+ * - Errors can be checked/handled during processing via the `errors` property
17
26
  *
18
- * Accepts items for mapping in the background, discards the results,
19
- * but accumulates exceptions in the `errors` property.
27
+ * Key Differences from `IterableQueueMapper`:
28
+ * - `maxUnread` defaults to equal `concurrency` (simplifying queue management)
29
+ * - Results are automatically iterated and discarded (all work should happen in mapper)
30
+ * - Errors are collected rather than thrown (available via errors property)
20
31
  *
21
- * Allows up to `concurrency` mappers to be in progress before
22
- * `enqueue` will block until a mapper completes.
32
+ * ### Usage
33
+ * - Items are added to the queue via the `await enqueue()` method
34
+ * - Check `errors` property to see if any errors occurred, stop if desired
35
+ * - IMPORTANT: `await enqueue()` method will block until a slot is available, if queue is full
36
+ * - IMPORTANT: Always `await onIdle()` to ensure all items are processed
37
+ *
38
+ * Note: the name is somewhat of a misnomer as this wraps `IterableQueueMapper`
39
+ * but is not itself an `Iterable`.
23
40
  *
24
41
  * @category Enqueue Input
42
+ *
43
+ * @see {@link IterableQueueMapper} for related class with more configuration options
44
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
25
45
  */
26
46
  class IterableQueueMapperSimple {
27
47
  /**
28
- * Create a new `IterableQueueMapperSimple`
29
- *
30
- * @param mapper Function which is called for every item in `input`.
31
- * Expected to return a `Promise` or value.
48
+ * Create a new `IterableQueueMapperSimple`, which uses `IterableQueueMapper` underneath, but
49
+ * automatically iterates and discards results as they complete.
32
50
  *
33
- * The `mapper` *should* handle all errors and not allow an error to be thrown
34
- * out of the `mapper` function as this enables the best handling of errors
35
- * closest to the time that they occur.
36
- *
37
- * If the `mapper` function does allow an error to be thrown then the
38
- * errors will be accumulated in the `errors` property.
51
+ * @param mapper Function called for every enqueued item. Returns a `Promise` or value.
39
52
  * @param options IterableQueueMapperSimple options
53
+ *
54
+ * @see {@link IterableQueueMapperSimple} for full class documentation
55
+ * @see {@link IterableQueueMapper} for related class with more configuration options
56
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
40
57
  */
41
58
  constructor(mapper, options = {}) {
42
59
  this._errors = [];
@@ -85,7 +102,7 @@ class IterableQueueMapperSimple {
85
102
  /**
86
103
  * Accept a request for sending in the background if a concurrency slot is available.
87
104
  * Else, do not return until a concurrency slot is freed up.
88
- * This provides concurrency background writes with back pressure to prevent
105
+ * This provides concurrency background writes with backpressure to prevent
89
106
  * the caller from getting too far ahead.
90
107
  *
91
108
  * MUST await `onIdle` for background `mappers`s to finish
@@ -1 +1 @@
1
- {"version":3,"file":"iterable-queue-mapper-simple.js","sourceRoot":"","sources":["../../src/iterable-queue-mapper-simple.ts"],"names":[],"mappings":";;;AACA,mEAA8D;AAK9D,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAa,yBAAyB;IAOpC;;;;;;;;;;;;;OAaG;IACH,YACE,MAA6B,EAC7B,UAMI,EAAE;QA3BS,YAAO,GAAoB,EAAE,CAAC;QAGvC,YAAO,GAAG,KAAK,CAAC;QA0BtB,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;QAEpC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,IAAI,2CAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAE7F,6BAA6B;QAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,6DAA6D;QAC7D,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE;YACzB,+BAA+B;YAC/B,oEAAoE;YACpE,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAClC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,IAAa,EAAE,KAAa;QAC/C,IAAI;YACF,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,8DAA8D;SAC/D;QAAC,OAAO,KAAU,EAAE;YACnB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;SACpC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,OAAO,CAAC,IAAa;QAChC,4EAA4E;QAC5E,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM;QACjB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,4CAA4C;QAC5C,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAEpB,MAAM,IAAI,CAAC,KAAK,CAAC;QAEjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAlHD,8DAkHC"}
1
+ {"version":3,"file":"iterable-queue-mapper-simple.js","sourceRoot":"","sources":["../../src/iterable-queue-mapper-simple.ts"],"names":[],"mappings":";;;AACA,mEAA8D;AAK9D,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAOpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAa,yBAAyB;IAOpC;;;;;;;;;;OAUG;IACH,YAAY,MAA6B,EAAE,UAA4C,EAAE;QAhBxE,YAAO,GAAoB,EAAE,CAAC;QAGvC,YAAO,GAAG,KAAK,CAAC;QActB,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;QAEpC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,IAAI,2CAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAE7F,6BAA6B;QAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,6DAA6D;QAC7D,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE;YACzB,+BAA+B;YAC/B,oEAAoE;YACpE,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAClC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,IAAa,EAAE,KAAa;QAC/C,IAAI;YACF,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,8DAA8D;SAC/D;QAAC,OAAO,KAAU,EAAE;YACnB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;SACpC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,OAAO,CAAC,IAAa;QAChC,4EAA4E;QAC5E,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM;QACjB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,4CAA4C;QAC5C,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAEpB,MAAM,IAAI,CAAC,KAAK,CAAC;QAEjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAtGD,8DAsGC"}
@@ -1,77 +1,57 @@
1
- import { Mapper } from './iterable-mapper';
2
- export interface IterableQueueMapperOptions {
3
- /**
4
- * Number of concurrently pending promises returned by `mapper`.
5
- *
6
- * Must be an integer from 1 and up or `Infinity`, must be <= `maxUnread`.
7
- *
8
- * @default 4
9
- */
10
- readonly concurrency?: number;
11
- /**
12
- * Number of pending unread iterable items.
13
- *
14
- * Must be an integer from 1 and up or `Infinity`, must be >= `concurrency`.
15
- *
16
- * @default 8
17
- */
18
- readonly maxUnread?: number;
19
- /**
20
- * When set to `false`, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an [aggregated error](https://github.com/sindresorhus/aggregate-error) containing all the errors from the rejected promises.
21
- *
22
- * @default true
23
- */
24
- readonly stopOnMapperError?: boolean;
25
- }
1
+ import { IterableMapperOptions, Mapper } from './iterable-mapper';
2
+ /**
3
+ * Options for IterableQueueMapper
4
+ */
5
+ export type IterableQueueMapperOptions = IterableMapperOptions;
26
6
  /**
27
7
  * Accepts queue items via `enqueue` and calls the `mapper` on them
28
- * with specified concurrency, storing the
29
- * `mapper` result in a queue of specified max size, before
30
- * being iterated / read by the caller. The `enqueue` method will block if
31
- * the queue is full, until an item is read.
8
+ * with specified `concurrency`, storing the `mapper` result in a queue
9
+ * of `maxUnread` size, before being iterated / read by the caller.
10
+ * The `enqueue` method will block if the queue is full, until an item is read.
32
11
  *
33
12
  * @remarks
34
13
  *
35
- * This allows performing a concurrent mapping with
36
- * back pressure for items added after queue creation
37
- * via a method call.
14
+ * ### Typical Use Case
15
+ * - Pushing items to an async I/O destination
16
+ * - In the simple sequential (`concurrency: 1`) case, allows 1 item to be flushed async while caller prepares next item
17
+ * - Results of the flushed items are needed in a subsequent step (if they are not, use `IterableQueueMapperSimple`)
18
+ * - Prevents the producer from racing ahead of the consumer if `maxUnread` is reached
19
+ *
20
+ * ### Error Handling
21
+ * The mapper should ideally handle all errors internally to enable error handling
22
+ * closest to where they occur. However, if errors do escape the mapper:
38
23
  *
39
- * Because items are added via a method call it is possible to
40
- * chain an `IterableMapper` that prefetches files and processes them,
41
- * with an `IterableQueueMapper` that processes the results of the
42
- * `mapper` function of the `IterableMapper`.
24
+ * When `stopOnMapperError` is true (default):
25
+ * - First error immediately stops processing
26
+ * - Error is thrown from the `AsyncIterator`'s next() call
43
27
  *
44
- * Typical use case is for a `background uploader` that prevents
45
- * the producer from racing ahead of the upload process, consuming
46
- * too much memory or disk space. As items are ready for upload
47
- * they are added to the queue with the `enqueue` method, which is
48
- * `await`ed by the caller. If the queue has room then `enqueue`
49
- * will return immediately, otherwise it will block until there is room.
28
+ * When `stopOnMapperError` is false:
29
+ * - Processing continues despite errors
30
+ * - All errors are collected and thrown together
31
+ * - Errors are thrown as `AggregateError` after all items complete
32
+ *
33
+ * ### Usage
34
+ * - Items are added to the queue via the `await enqueue()` method
35
+ * - IMPORTANT: `await enqueue()` method will block until a slot is available, if queue is full
36
+ * - Call `done()` when no more items will be enqueued
37
+ * - IMPORTANT: Always `await onIdle()` to ensure all items are processed
50
38
  *
51
39
  * @category Enqueue Input
40
+ *
41
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
52
42
  */
53
43
  export declare class IterableQueueMapper<Element, NewElement> implements AsyncIterable<NewElement> {
54
44
  private _iterableMapper;
55
45
  private _sourceIterable;
56
46
  /**
57
- * Create a new `IterableQueueMapper`
58
- *
59
- * @param mapper Function which is called for every item in `input`.
60
- * Expected to return a `Promise` or value.
47
+ * Create a new `IterableQueueMapper`, which uses `IterableMapper` underneath, and exposes a
48
+ * queue interface for adding items that are not exposed via an iterator.
61
49
  *
62
- * The `mapper` *should* handle all errors and not allow an error to be thrown
63
- * out of the `mapper` function as this enables the best handling of errors
64
- * closest to the time that they occur.
65
- *
66
- * If the `mapper` function does allow an error to be thrown then the
67
- * `stopOnMapperError` option controls the behavior:
68
- * - `stopOnMapperError`: `true` - will throw the error
69
- * out of `next` or the `AsyncIterator` returned from `[Symbol.asyncIterator]`
70
- * and stop processing.
71
- * - `stopOnMapperError`: `false` - will continue processing
72
- * and accumulate the errors to be thrown from `next` or the `AsyncIterator`
73
- * returned from `[Symbol.asyncIterator]` when all items have been processed.
50
+ * @param mapper Function called for every enqueued item. Returns a `Promise` or value.
74
51
  * @param options IterableQueueMapper options
52
+ *
53
+ * @see {@link IterableQueueMapper} for full class documentation
54
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
75
55
  */
76
56
  constructor(mapper: Mapper<Element, NewElement>, options?: IterableQueueMapperOptions);
77
57
  [Symbol.asyncIterator](): AsyncIterator<NewElement>;
@@ -8,51 +8,51 @@ const iterable_mapper_1 = require("./iterable-mapper");
8
8
  const iterable_queue_1 = require("./iterable-queue");
9
9
  /**
10
10
  * Accepts queue items via `enqueue` and calls the `mapper` on them
11
- * with specified concurrency, storing the
12
- * `mapper` result in a queue of specified max size, before
13
- * being iterated / read by the caller. The `enqueue` method will block if
14
- * the queue is full, until an item is read.
11
+ * with specified `concurrency`, storing the `mapper` result in a queue
12
+ * of `maxUnread` size, before being iterated / read by the caller.
13
+ * The `enqueue` method will block if the queue is full, until an item is read.
15
14
  *
16
15
  * @remarks
17
16
  *
18
- * This allows performing a concurrent mapping with
19
- * back pressure for items added after queue creation
20
- * via a method call.
17
+ * ### Typical Use Case
18
+ * - Pushing items to an async I/O destination
19
+ * - In the simple sequential (`concurrency: 1`) case, allows 1 item to be flushed async while caller prepares next item
20
+ * - Results of the flushed items are needed in a subsequent step (if they are not, use `IterableQueueMapperSimple`)
21
+ * - Prevents the producer from racing ahead of the consumer if `maxUnread` is reached
21
22
  *
22
- * Because items are added via a method call it is possible to
23
- * chain an `IterableMapper` that prefetches files and processes them,
24
- * with an `IterableQueueMapper` that processes the results of the
25
- * `mapper` function of the `IterableMapper`.
23
+ * ### Error Handling
24
+ * The mapper should ideally handle all errors internally to enable error handling
25
+ * closest to where they occur. However, if errors do escape the mapper:
26
26
  *
27
- * Typical use case is for a `background uploader` that prevents
28
- * the producer from racing ahead of the upload process, consuming
29
- * too much memory or disk space. As items are ready for upload
30
- * they are added to the queue with the `enqueue` method, which is
31
- * `await`ed by the caller. If the queue has room then `enqueue`
32
- * will return immediately, otherwise it will block until there is room.
27
+ * When `stopOnMapperError` is true (default):
28
+ * - First error immediately stops processing
29
+ * - Error is thrown from the `AsyncIterator`'s next() call
30
+ *
31
+ * When `stopOnMapperError` is false:
32
+ * - Processing continues despite errors
33
+ * - All errors are collected and thrown together
34
+ * - Errors are thrown as `AggregateError` after all items complete
35
+ *
36
+ * ### Usage
37
+ * - Items are added to the queue via the `await enqueue()` method
38
+ * - IMPORTANT: `await enqueue()` method will block until a slot is available, if queue is full
39
+ * - Call `done()` when no more items will be enqueued
40
+ * - IMPORTANT: Always `await onIdle()` to ensure all items are processed
33
41
  *
34
42
  * @category Enqueue Input
43
+ *
44
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
35
45
  */
36
46
  class IterableQueueMapper {
37
47
  /**
38
- * Create a new `IterableQueueMapper`
48
+ * Create a new `IterableQueueMapper`, which uses `IterableMapper` underneath, and exposes a
49
+ * queue interface for adding items that are not exposed via an iterator.
39
50
  *
40
- * @param mapper Function which is called for every item in `input`.
41
- * Expected to return a `Promise` or value.
42
- *
43
- * The `mapper` *should* handle all errors and not allow an error to be thrown
44
- * out of the `mapper` function as this enables the best handling of errors
45
- * closest to the time that they occur.
46
- *
47
- * If the `mapper` function does allow an error to be thrown then the
48
- * `stopOnMapperError` option controls the behavior:
49
- * - `stopOnMapperError`: `true` - will throw the error
50
- * out of `next` or the `AsyncIterator` returned from `[Symbol.asyncIterator]`
51
- * and stop processing.
52
- * - `stopOnMapperError`: `false` - will continue processing
53
- * and accumulate the errors to be thrown from `next` or the `AsyncIterator`
54
- * returned from `[Symbol.asyncIterator]` when all items have been processed.
51
+ * @param mapper Function called for every enqueued item. Returns a `Promise` or value.
55
52
  * @param options IterableQueueMapper options
53
+ *
54
+ * @see {@link IterableQueueMapper} for full class documentation
55
+ * @see {@link IterableMapper} for underlying mapper implementation and examples of combined usage
56
56
  */
57
57
  constructor(mapper, options = {}) {
58
58
  this._sourceIterable = new iterable_queue_1.IterableQueue({
@@ -1 +1 @@
1
- {"version":3,"file":"iterable-queue-mapper.js","sourceRoot":"","sources":["../../src/iterable-queue-mapper.ts"],"names":[],"mappings":";;;AAAA,EAAE;AACF,sGAAsG;AACtG,EAAE;AACF,uDAA2D;AAC3D,qDAAiD;AA6BjD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,mBAAmB;IAK9B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,YAAY,MAAmC,EAAE,UAAsC,EAAE;QACvF,IAAI,CAAC,eAAe,GAAG,IAAI,8BAAa,CAAC;YACvC,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,GAAG,IAAI,gCAAc,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACnF,CAAC;IAEM,CAAC,MAAM,CAAC,aAAa,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,IAAI;QACf,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAAC,IAAa;QAChC,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACI,IAAI;QACT,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF;AA9DD,kDA8DC"}
1
+ {"version":3,"file":"iterable-queue-mapper.js","sourceRoot":"","sources":["../../src/iterable-queue-mapper.ts"],"names":[],"mappings":";;;AAAA,EAAE;AACF,sGAAsG;AACtG,EAAE;AACF,uDAAkF;AAClF,qDAAiD;AAOjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAa,mBAAmB;IAK9B;;;;;;;;;OASG;IACH,YAAY,MAAmC,EAAE,UAAsC,EAAE;QACvF,IAAI,CAAC,eAAe,GAAG,IAAI,8BAAa,CAAC;YACvC,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,GAAG,IAAI,gCAAc,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACnF,CAAC;IAEM,CAAC,MAAM,CAAC,aAAa,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,IAAI;QACf,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAAC,IAAa;QAChC,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACI,IAAI;QACT,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF;AApDD,kDAoDC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shutterstock/p-map-iterable",
3
- "version": "1.1.0",
4
- "description": "Set of classes that allow you to call mappers with controlled concurrency on iterables or on queues",
3
+ "version": "1.1.2",
4
+ "description": "Set of classes used for async prefetching with backpressure (IterableMapper) and async flushing with backpressure (IterableQueueMapper, IterableQueueMapperSimple)",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
7
7
  "publishConfig": {
@@ -24,7 +24,7 @@
24
24
  "AsyncIterator"
25
25
  ],
26
26
  "scripts": {
27
- "build": "tsc --build tsconfig.json && echo 'examples/\n*.tsbuildinfo' > dist/.npmignore",
27
+ "build": "tsc --build tsconfig.json && echo 'examples/\n*.tsbuildinfo\n*.test.*\n*.d.ts.map' > dist/.npmignore",
28
28
  "build:docs": "typedoc src/index.ts",
29
29
  "example:iterable-mapper": "ts-node -r tsconfig-paths/register examples/iterable-mapper.ts",
30
30
  "example:iterable-queue-mapper": "ts-node -r tsconfig-paths/register examples/iterable-queue-mapper.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"blocking-queue.d.ts","sourceRoot":"","sources":["../../src/blocking-queue.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAOD;;;;;;;GAOG;AACH,qBAAa,aAAa,CAAC,OAAO;IAChC,OAAO,CAAC,QAAQ,CAAiC;IAKjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAO5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0C;IAO1E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0C;IAE1E,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;OAIG;gBACS,OAAO,GAAE,oBAAyB;IAuB9C,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;OAIG;IACI,IAAI,IAAI,IAAI;IAcnB;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BlD;;;;OAIG;IACU,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAoCpD,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,SAAS;IAqCjB;;OAEG;YACW,YAAY;IAW1B;;OAEG;YACW,WAAW;CAU1B"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=blocking-queue.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blocking-queue.test.d.ts","sourceRoot":"","sources":["../../src/blocking-queue.test.ts"],"names":[],"mappings":""}
@@ -1,174 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /// <reference types="jest" />
4
- const blocking_queue_1 = require("./blocking-queue");
5
- describe('BlockingQueue', () => {
6
- beforeAll(() => {
7
- // nothing
8
- });
9
- beforeEach(() => {
10
- jest.clearAllMocks();
11
- });
12
- describe('maxUnread: 0', () => {
13
- it('single item enqueue/dequeue works', async () => {
14
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
15
- setTimeout(() => {
16
- void queue.enqueue(1);
17
- }, 1000);
18
- const item = await queue.dequeue();
19
- queue.done();
20
- expect(item).toBe(1);
21
- });
22
- it('dequeue after done does not hang', async () => {
23
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
24
- setTimeout(() => {
25
- void queue.enqueue(1);
26
- }, 1000);
27
- const item = await queue.dequeue();
28
- queue.done();
29
- await queue.dequeue();
30
- expect(item).toBe(1);
31
- });
32
- it('enqueue after done throws', async () => {
33
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
34
- setTimeout(() => {
35
- void queue.enqueue(1);
36
- }, 1000);
37
- const item = await queue.dequeue();
38
- queue.done();
39
- await expect(async () => queue.enqueue(2)).rejects.toThrowError('`enqueue` called after `done` called');
40
- expect(item).toBe(1);
41
- });
42
- it('balanced enqueue/dequeue works', async () => {
43
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
44
- const writers = [];
45
- writers.push(queue.enqueue(1));
46
- writers.push(queue.enqueue(2));
47
- const readers = [];
48
- readers.push(queue.dequeue());
49
- readers.push(queue.dequeue());
50
- await Promise.all(writers);
51
- queue.done();
52
- await Promise.all(readers);
53
- expect(await readers[0]).toBe(1);
54
- expect(await readers[1]).toBe(2);
55
- });
56
- it('full queue blocks enqueue until dequeue', async () => {
57
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
58
- // Add first item
59
- void queue.enqueue(1);
60
- // Do not wait for second item to add (it will wait until an enqueue completes)
61
- setTimeout(() => {
62
- void queue.enqueue(2);
63
- }, 2000);
64
- const startTime = Date.now();
65
- expect(await queue.dequeue()).toBe(1);
66
- expect(Date.now() - startTime).toBeLessThan(2000);
67
- expect(await queue.dequeue()).toBe(2);
68
- expect(Date.now() - startTime).toBeGreaterThanOrEqual(2000);
69
- queue.done();
70
- });
71
- });
72
- describe('maxUnread: 1', () => {
73
- it('single item enqueue/dequeue works', async () => {
74
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
75
- await queue.enqueue(1);
76
- const item = await queue.dequeue();
77
- queue.done();
78
- expect(item).toBe(1);
79
- });
80
- it('dequeue after done does not hang', async () => {
81
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
82
- await queue.enqueue(1);
83
- const item = await queue.dequeue();
84
- queue.done();
85
- await queue.dequeue();
86
- expect(item).toBe(1);
87
- });
88
- it('dequeue after done and empty does not hang', async () => {
89
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
90
- await queue.enqueue(1);
91
- const item = await queue.dequeue();
92
- queue.done();
93
- await queue.dequeue();
94
- expect(item).toBe(1);
95
- await queue.dequeue();
96
- });
97
- it('enqueue after done throws', async () => {
98
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
99
- await queue.enqueue(1);
100
- const item = await queue.dequeue();
101
- queue.done();
102
- await expect(async () => queue.enqueue(2)).rejects.toThrowError('`enqueue` called after `done` called');
103
- expect(item).toBe(1);
104
- });
105
- it('balanced enqueue/dequeue works', async () => {
106
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
107
- const writers = [];
108
- writers.push(queue.enqueue(1));
109
- writers.push(queue.enqueue(2));
110
- const readers = [];
111
- readers.push(queue.dequeue());
112
- readers.push(queue.dequeue());
113
- await Promise.all(writers);
114
- queue.done();
115
- await Promise.all(readers);
116
- expect(await readers[0]).toBe(1);
117
- expect(await readers[1]).toBe(2);
118
- });
119
- it('more dequeue than enqueue works', async () => {
120
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
121
- const writers = [];
122
- writers.push(queue.enqueue(1));
123
- writers.push(queue.enqueue(2));
124
- const readers = [];
125
- readers.push(queue.dequeue());
126
- readers.push(queue.dequeue());
127
- readers.push(queue.dequeue());
128
- readers.push(queue.dequeue());
129
- await Promise.all(writers);
130
- queue.done();
131
- await Promise.all(readers);
132
- expect(await readers[0]).toBe(1);
133
- expect(await readers[1]).toBe(2);
134
- expect(await readers[2]).toBeUndefined();
135
- expect(await readers[3]).toBeUndefined();
136
- });
137
- it('full queue blocks enqueue until dequeue', async () => {
138
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
139
- // Wait for first item to add
140
- await queue.enqueue(1);
141
- // Do not wait for second item to add (it will wait until an enqueue completes)
142
- setTimeout(() => {
143
- void queue.enqueue(2);
144
- }, 2000);
145
- expect(await queue.dequeue()).toBe(1);
146
- const startTime = Date.now();
147
- expect(await queue.dequeue()).toBe(2);
148
- const duration = Date.now() - startTime;
149
- expect(duration).toBeGreaterThanOrEqual(2000);
150
- queue.done();
151
- });
152
- });
153
- describe('maxUnread: 2', () => {
154
- it('no reads until done works', async () => {
155
- const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 2 });
156
- const writers = [];
157
- writers.push(queue.enqueue(1));
158
- writers.push(queue.enqueue(2));
159
- await Promise.all(writers);
160
- queue.done();
161
- const readers = [];
162
- readers.push(queue.dequeue());
163
- readers.push(queue.dequeue());
164
- readers.push(queue.dequeue());
165
- readers.push(queue.dequeue());
166
- await Promise.all(readers);
167
- expect(await readers[0]).toBe(1);
168
- expect(await readers[1]).toBe(2);
169
- expect(await readers[2]).toBeUndefined();
170
- expect(await readers[3]).toBeUndefined();
171
- });
172
- });
173
- });
174
- //# sourceMappingURL=blocking-queue.test.js.map