@shutterstock/p-map-iterable 1.0.11 → 1.1.1

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,25 +1,116 @@
1
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) [![API Docs](https://img.shields.io/badge/API%20Docs-View%20Here-blue)](https://tech.shutterstock.com/p-map-iterable/) [![Build - CI](https://github.com/shutterstock/p-map-iterable/actions/workflows/ci.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/ci.yml) [![Package and Publish](https://github.com/shutterstock/p-map-iterable/actions/workflows/publish.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/publish.yml) [![Publish Docs](https://github.com/shutterstock/p-map-iterable/actions/workflows/docs.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/docs.yml)
1
+ [![npm (scoped)](https://img.shields.io/npm/v/%40shutterstock/p-map-iterable)](https://www.npmjs.com/package/@shutterstock/p-map-iterable) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) [![API Docs](https://img.shields.io/badge/API%20Docs-View%20Here-blue)](https://tech.shutterstock.com/p-map-iterable/) [![Build - CI](https://github.com/shutterstock/p-map-iterable/actions/workflows/ci.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/ci.yml) [![Package and Publish](https://github.com/shutterstock/p-map-iterable/actions/workflows/publish.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/publish.yml) [![Publish Docs](https://github.com/shutterstock/p-map-iterable/actions/workflows/docs.yml/badge.svg)](https://github.com/shutterstock/p-map-iterable/actions/workflows/docs.yml)
2
2
 
3
3
  # Overview
4
4
 
5
- `@shutterstock/p-map-iterable` provides several classes that allow processing results of `p-map`-style mapper functions by iterating the results as they are completed, with back pressure to limit the number of items that are processed ahead of the consumer.
5
+ `@shutterstock/p-map-iterable` provides several classes that allow processing results of `p-map`-style mapper functions by iterating the results as they are completed, with backpressure to limit the number of items that are processed ahead of the consumer.
6
6
 
7
7
  A common use case for `@shutterstock/p-map-iterable` is as a "prefetcher" that will fetch, for example, AWS S3 files in an AWS Lambda function. By prefetching large files the consumer is able to use 100% of the paid-for Lambda CPU time for the JS thread, rather than waiting idle while the next file is fetched. The backpressure (set by `maxUnread`) prevents the prefetcher from consuming unlimited memory or disk space by racing ahead of the consumer.
8
8
 
9
9
  These classes will typically be helpful in batch or queue consumers, not as much in request/response services.
10
10
 
11
- # Example Usage Scenario
11
+ # Example Usage Scenarios
12
12
 
13
- - AWS Lambda function for Kafka or Kinesis stream processing
14
- - Each invocation has a batch of, say, 100 records
15
- - Each record points to a file on S3 that needs to be parsed, processed, then written back to S3
16
- - The average file size is 50 MB, so these will take a few seconds to fetch and write
17
- - The Lambda function has 1,769 MB of RAM which gives it 1 vCPU, allowing the JS thread to run at roughly 100% speed of 1 core
18
- - If the code never waits to fetch or write files then the Lambda function will be able to use 100% of the paid-for CPU time
19
- - If there is no back pressure then reading 100 * 50 MB files would fill up the default Lambda temp disk space of 512 MB OR would consume 5 GB of memory, which would cause the Lambda function to fail
20
- - We can use `IterableMapper` to fetch up to 5 of the next files while the current file is being processed, then pause until the current file is processed and the next file is consumed
21
- - We can also use `IterableQueueMapper.enqueue()` to write the files back to S3 without waiting for them to finish, unless we get more than, say, 3-4 files being uploaded at once, at which point we can pause until the current file is uploaded before we allow queuing another file for upload
22
- - In the rare case that 3-4 files are uploading, but not yet finished, we would block on `.enqueue()` and not consume much CPU while waiting for at least 1 upload to finish
13
+ ## Typical Processing Loop without `IterableMapper`
14
+
15
+ ```typescript
16
+ const source = new SomeSource();
17
+ const sourceIds = [1, 2,... 1000];
18
+ const sink = new SomeSink();
19
+ for (const sourceId of sourceIds) {
20
+ const item = await source.read(sourceId); // takes 300 ms of I/O wait, no CPU
21
+ const outputItem = doSomeOperation(item); // takes 20 ms of CPU
22
+ await sink.write(outputItem); // takes 500 ms of I/O wait, no CPU
23
+ }
24
+ ```
25
+
26
+ Each iteration takes 820ms total, but we waste time waiting for I/O. We could prefetch the next read (300ms) while processing (20ms) and writing (500ms), without changing the order of reads or writes.
27
+
28
+ ## Using `IterableMapper` as Prefetcher with Blocking Sequential Writes
29
+
30
+ `concurrency: 1` on the prefetcher preserves the order of the reads and and writes are sequential and blocking (unchanged).
31
+
32
+ ```typescript
33
+ const source = new SomeSource();
34
+ const sourceIds = [1, 2,... 1000];
35
+ // Pre-reads up to 8 items serially and releases in sequential order
36
+ const sourcePrefetcher = new IterableMapper(sourceIds,
37
+ async (sourceId) => source.read(sourceId),
38
+ { concurrency: 1, maxUnread: 10 }
39
+ );
40
+ const sink = new SomeSink();
41
+ for await (const item of sourcePrefetcher) { // may not block for fast sources
42
+ const outputItem = doSomeOperation(item); // takes 20 ms of CPU
43
+ await sink.write(outputItem); // takes 500 ms of I/O wait, no CPU
44
+ }
45
+ ```
46
+
47
+ This reduces iteration time to 520ms by overlapping reads with processing/writing.
48
+
49
+ ## Using `IterableMapper` as Prefetcher with Background Sequential Writes with `IterableQueueMapperSimple`
50
+
51
+ `concurrency: 1` on the prefetcher preserves the order of the reads.
52
+ `concurrency: 1` on the flusher preserves the order of the writes, but allows the loop to iterate while last write is completing.
53
+
54
+ ```typescript
55
+ const source = new SomeSource();
56
+ const sourceIds = [1, 2,... 1000];
57
+ const sourcePrefetcher = new IterableMapper(sourceIds,
58
+ async (sourceId) => source.read(sourceId),
59
+ { concurrency: 1, maxUnread: 10 }
60
+ );
61
+ const sink = new SomeSink();
62
+ const flusher = new IterableQueueMapperSimple(
63
+ async (outputItem) => sink.write(outputItem),
64
+ { concurrency: 1 }
65
+ );
66
+ for await (const item of sourcePrefetcher) { // may not block for fast sources
67
+ const outputItem = doSomeOperation(item); // takes 20 ms of CPU
68
+ await flusher.enqueue(outputItem); // will periodically block for portion of write time
69
+ }
70
+ // Wait for all writes to complete
71
+ await flusher.onIdle();
72
+ // Check for errors
73
+ if (flusher.errors.length > 0) {
74
+ // ...
75
+ }
76
+ ```
77
+
78
+ This reduces iteration time to about `max((max(readTime, writeTime) - cpuOpTime, cpuOpTime))`
79
+ by overlapping reads and writes with the CPU processing step.
80
+ In this contrived example, the loop time is reduced to 500ms - 20ms = 480ms.
81
+ In cases where the CPU usage time is higher, the impact can be greater.
82
+
83
+ ## Using `IterableMapper` as Prefetcher with Out of Order Reads and Background Out of Order Writes with `IterableQueueMapperSimple`
84
+
85
+ For maximum throughput, allow out of order reads and writes with
86
+ `IterableQueueMapper` (to iterate results with backpressure when too many unread items) or
87
+ `IterableQueueMapperSimple` (to handle errors at end without custom iteration and applying backpressure to block further enqueues when `concurrency` items are in process):
88
+
89
+ ```typescript
90
+ const source = new SomeSource();
91
+ const sourceIds = [1, 2,... 1000];
92
+ const sourcePrefetcher = new IterableMapper(sourceIds,
93
+ async (sourceId) => source.read(sourceId),
94
+ { concurrency: 10, maxUnread: 20 }
95
+ );
96
+ const sink = new SomeSink();
97
+ const flusher = new IterableQueueMapperSimple(
98
+ async (outputItem) => sink.write(outputItem),
99
+ { concurrency: 10 }
100
+ );
101
+ for await (const item of sourcePrefetcher) { // typically will not block
102
+ const outputItem = doSomeOperation(item); // takes 20 ms of CPU
103
+ await flusher.enqueue(outputItem); // typically will not block
104
+ }
105
+ // Wait for all writes to complete
106
+ await flusher.onIdle();
107
+ // Check for errors
108
+ if (flusher.errors.length > 0) {
109
+ // ...
110
+ }
111
+ ```
112
+
113
+ This reduces iteration time to about 20ms by overlapping reads and writes with the CPU processing step. In this contrived (but common) example we would get a 41x improvement in throughput, removing 97.5% of the time to process each item and fully utilizing the CPU time available in the JS event loop.
23
114
 
24
115
  # Getting Started
25
116
 
@@ -32,7 +123,10 @@ The package is available on npm as [@shutterstock/p-map-iterable](https://www.np
32
123
  ## Importing
33
124
 
34
125
  ```typescript
35
- import { IterableMapper, IterableQueueMapper, IterableQueueMapperSimple } from '@shutterstock/p-map-iterable';
126
+ import {
127
+ IterableMapper,
128
+ IterableQueueMapper,
129
+ IterableQueueMapperSimple } from '@shutterstock/p-map-iterable';
36
130
  ```
37
131
 
38
132
  ## API Documentation
@@ -63,7 +157,7 @@ These diagrams illustrate the differences in operation betweeen `p-map`, `p-queu
63
157
  - User supplied sync or async mapper function
64
158
  - Exposes an async iterable interface for consuming mapped items
65
159
  - Allows a maximum queue depth of mapped items - if the consumer stops consuming, the queue will fill up, at which point the mapper will stop being invoked until an item is consumed from the queue
66
- - This allows mapping with back pressure so that the mapper does not consume unlimited resources (e.g. memory, disk, network, event loop time) by racing ahead of the consumer
160
+ - This allows mapping with backpressure so that the mapper does not consume unlimited resources (e.g. memory, disk, network, event loop time) by racing ahead of the consumer
67
161
  - [IterableQueueMapper](https://tech.shutterstock.com/p-map-iterable/classes/IterableQueueMapper.html)
68
162
  - Wraps `IterableMapper`
69
163
  - Adds items to the queue via the `enqueue` method
@@ -88,7 +182,7 @@ These diagrams illustrate the differences in operation betweeen `p-map`, `p-queu
88
182
 
89
183
  See [p-map](https://github.com/sindresorhus/p-map) docs for a good start in understanding what this does.
90
184
 
91
- The key difference between `IterableMapper` and `pMap` are that `IterableMapper` does not return when the entire mapping is done, rather it exposes an iterable that the caller loops through. This enables results to be processed while the mapping is still happening, while optionally allowing for back pressure to slow or stop the mapping if the caller is not consuming items fast enough. Common use cases include `prefetching` items from a remote service - the next set of requests are dispatched asyncronously while the current responses are processed and the prefetch requests will pause when the unread queue fills up.
185
+ The key difference between `IterableMapper` and `pMap` are that `IterableMapper` does not return when the entire mapping is done, rather it exposes an iterable that the caller loops through. This enables results to be processed while the mapping is still happening, while optionally allowing for backpressure to slow or stop the mapping if the caller is not consuming items fast enough. Common use cases include `prefetching` items from a remote service - the next set of requests are dispatched asyncronously while the current responses are processed and the prefetch requests will pause when the unread queue fills up.
92
186
 
93
187
  See [examples/iterable-mapper.ts](./examples/iterable-mapper.ts) for an example.
94
188
 
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BlockingQueue = void 0;
4
- const tslib_1 = require("tslib");
5
4
  const queue_1 = require("./queue");
6
5
  /**
7
6
  * `enqueue` blocks when the queue is full, until an item is read with `dequeue`, or done
@@ -72,65 +71,61 @@ class BlockingQueue {
72
71
  *
73
72
  * @param item Element to add
74
73
  */
75
- enqueue(item) {
76
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
77
- if (this._doneAdding) {
78
- throw new Error('`enqueue` called after `done` called');
79
- }
80
- // We always add to the internal queue, we just wait to return if the queue is full
81
- this._unreadQueue.enqueue(item);
82
- if (this._readersWaiting.length > 0) {
83
- // Release a reader if one is waiting for an item
84
- // Remove the first waiting reader Promise from the queue
85
- const reader = this._readersWaiting.dequeue();
86
- if (reader === undefined) {
87
- throw new TypeError('reader queue returned undefined');
88
- }
89
- // Resolve the Promise for a waiting reader
90
- reader(undefined);
91
- }
92
- if (this._unreadQueue.length > this._options.maxUnread) {
93
- // We wait if the queue is at max length
94
- yield this.waitForRead();
74
+ async enqueue(item) {
75
+ if (this._doneAdding) {
76
+ throw new Error('`enqueue` called after `done` called');
77
+ }
78
+ // We always add to the internal queue, we just wait to return if the queue is full
79
+ this._unreadQueue.enqueue(item);
80
+ if (this._readersWaiting.length > 0) {
81
+ // Release a reader if one is waiting for an item
82
+ // Remove the first waiting reader Promise from the queue
83
+ const reader = this._readersWaiting.dequeue();
84
+ if (reader === undefined) {
85
+ throw new TypeError('reader queue returned undefined');
95
86
  }
96
- });
87
+ // Resolve the Promise for a waiting reader
88
+ reader(undefined);
89
+ }
90
+ if (this._unreadQueue.length > this._options.maxUnread) {
91
+ // We wait if the queue is at max length
92
+ await this.waitForRead();
93
+ }
97
94
  }
98
95
  /**
99
96
  * Gets an element when one is ready, waits if none are ready.
100
97
  *
101
98
  * @returns Element or undefined if queue is empty and `done` has been called
102
99
  */
103
- dequeue() {
104
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
105
- // Bail out and release all waiters if there are no more items coming
106
- const done = this.areWeDone();
107
- if (done) {
108
- return undefined;
109
- }
110
- // Check if queue has an item
111
- let item;
112
- // Get the item if one is ready
113
- if (this._unreadQueue.length !== 0 && this._readersWaiting.length === 0) {
114
- // If there are items and no waiters, pop one and return it
115
- item = this._unreadQueue.dequeue();
116
- }
117
- else {
118
- // If there are no items and/or there are already waiters, we have to wait for one to be ready
119
- yield this.waitForWrite();
120
- item = this._unreadQueue.dequeue();
121
- }
122
- if (this._writersWaiting.length > 0) {
123
- // Release a writer if one is waiting
124
- // Remove the first waiting reader Promise from the queue
125
- const writer = this._writersWaiting.dequeue();
126
- if (writer === undefined) {
127
- throw new TypeError('writer queue returned undefined');
128
- }
129
- // Resolve the Promise for a waiting writer
130
- writer(undefined);
100
+ async dequeue() {
101
+ // Bail out and release all waiters if there are no more items coming
102
+ const done = this.areWeDone();
103
+ if (done) {
104
+ return undefined;
105
+ }
106
+ // Check if queue has an item
107
+ let item;
108
+ // Get the item if one is ready
109
+ if (this._unreadQueue.length !== 0 && this._readersWaiting.length === 0) {
110
+ // If there are items and no waiters, pop one and return it
111
+ item = this._unreadQueue.dequeue();
112
+ }
113
+ else {
114
+ // If there are no items and/or there are already waiters, we have to wait for one to be ready
115
+ await this.waitForWrite();
116
+ item = this._unreadQueue.dequeue();
117
+ }
118
+ if (this._writersWaiting.length > 0) {
119
+ // Release a writer if one is waiting
120
+ // Remove the first waiting reader Promise from the queue
121
+ const writer = this._writersWaiting.dequeue();
122
+ if (writer === undefined) {
123
+ throw new TypeError('writer queue returned undefined');
131
124
  }
132
- return item;
133
- });
125
+ // Resolve the Promise for a waiting writer
126
+ writer(undefined);
127
+ }
128
+ return item;
134
129
  }
135
130
  releaseAllReaders() {
136
131
  // Release all the waiting readers since we're done
@@ -172,30 +167,26 @@ class BlockingQueue {
172
167
  /**
173
168
  * Waits for an item to be ready in the queue
174
169
  */
175
- waitForWrite() {
176
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
177
- // If there are waiters, create a waiter promise and add to end of list
178
- // Note: the background reader removes the promise from the readers waiting queue
179
- const itemReady = new Promise((resolve) => {
180
- this._readersWaiting.enqueue(resolve);
181
- });
182
- // Wait for our item to be ready
183
- yield itemReady;
170
+ async waitForWrite() {
171
+ // If there are waiters, create a waiter promise and add to end of list
172
+ // Note: the background reader removes the promise from the readers waiting queue
173
+ const itemReady = new Promise((resolve) => {
174
+ this._readersWaiting.enqueue(resolve);
184
175
  });
176
+ // Wait for our item to be ready
177
+ await itemReady;
185
178
  }
186
179
  /**
187
180
  * Waits to be able to add an item to the queue when full
188
181
  */
189
- waitForRead() {
190
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
191
- // If there are waiters, create a waiter promise and add to end of list
192
- // Note: the background reader removes the promise from the readers waiting queue
193
- const writeReady = new Promise((resolve) => {
194
- this._writersWaiting.enqueue(resolve);
195
- });
196
- // Wait for our turn to add to the queue
197
- yield writeReady;
182
+ async waitForRead() {
183
+ // If there are waiters, create a waiter promise and add to end of list
184
+ // Note: the background reader removes the promise from the readers waiting queue
185
+ const writeReady = new Promise((resolve) => {
186
+ this._writersWaiting.enqueue(resolve);
198
187
  });
188
+ // Wait for our turn to add to the queue
189
+ await writeReady;
199
190
  }
200
191
  }
201
192
  exports.BlockingQueue = BlockingQueue;
@@ -1 +1 @@
1
- {"version":3,"file":"blocking-queue.js","sourceRoot":"","sources":["../../src/blocking-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blocking-queue.js","sourceRoot":"","sources":["../../src/blocking-queue.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
3
  /// <reference types="jest" />
5
4
  const blocking_queue_1 = require("./blocking-queue");
6
5
  describe('BlockingQueue', () => {
@@ -11,36 +10,36 @@ describe('BlockingQueue', () => {
11
10
  jest.clearAllMocks();
12
11
  });
13
12
  describe('maxUnread: 0', () => {
14
- it('single item enqueue/dequeue works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
13
+ it('single item enqueue/dequeue works', async () => {
15
14
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
16
15
  setTimeout(() => {
17
16
  void queue.enqueue(1);
18
17
  }, 1000);
19
- const item = yield queue.dequeue();
18
+ const item = await queue.dequeue();
20
19
  queue.done();
21
20
  expect(item).toBe(1);
22
- }));
23
- it('dequeue after done does not hang', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
21
+ });
22
+ it('dequeue after done does not hang', async () => {
24
23
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
25
24
  setTimeout(() => {
26
25
  void queue.enqueue(1);
27
26
  }, 1000);
28
- const item = yield queue.dequeue();
27
+ const item = await queue.dequeue();
29
28
  queue.done();
30
- yield queue.dequeue();
29
+ await queue.dequeue();
31
30
  expect(item).toBe(1);
32
- }));
33
- it('enqueue after done throws', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
31
+ });
32
+ it('enqueue after done throws', async () => {
34
33
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
35
34
  setTimeout(() => {
36
35
  void queue.enqueue(1);
37
36
  }, 1000);
38
- const item = yield queue.dequeue();
37
+ const item = await queue.dequeue();
39
38
  queue.done();
40
- yield expect(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { return queue.enqueue(2); })).rejects.toThrowError('`enqueue` called after `done` called');
39
+ await expect(async () => queue.enqueue(2)).rejects.toThrowError('`enqueue` called after `done` called');
41
40
  expect(item).toBe(1);
42
- }));
43
- it('balanced enqueue/dequeue works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
41
+ });
42
+ it('balanced enqueue/dequeue works', async () => {
44
43
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 0 });
45
44
  const writers = [];
46
45
  writers.push(queue.enqueue(1));
@@ -48,13 +47,13 @@ describe('BlockingQueue', () => {
48
47
  const readers = [];
49
48
  readers.push(queue.dequeue());
50
49
  readers.push(queue.dequeue());
51
- yield Promise.all(writers);
50
+ await Promise.all(writers);
52
51
  queue.done();
53
- yield Promise.all(readers);
54
- expect(yield readers[0]).toBe(1);
55
- expect(yield readers[1]).toBe(2);
56
- }));
57
- it('full queue blocks enqueue until dequeue', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
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 () => {
58
57
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
59
58
  // Add first item
60
59
  void queue.enqueue(1);
@@ -63,47 +62,47 @@ describe('BlockingQueue', () => {
63
62
  void queue.enqueue(2);
64
63
  }, 2000);
65
64
  const startTime = Date.now();
66
- expect(yield queue.dequeue()).toBe(1);
65
+ expect(await queue.dequeue()).toBe(1);
67
66
  expect(Date.now() - startTime).toBeLessThan(2000);
68
- expect(yield queue.dequeue()).toBe(2);
69
- expect(Date.now() - startTime).toBeGreaterThanOrEqual(2000);
67
+ expect(await queue.dequeue()).toBe(2);
68
+ expect(Math.ceil(Date.now() - startTime)).toBeGreaterThanOrEqual(2000);
70
69
  queue.done();
71
- }));
70
+ });
72
71
  });
73
72
  describe('maxUnread: 1', () => {
74
- it('single item enqueue/dequeue works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
73
+ it('single item enqueue/dequeue works', async () => {
75
74
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
76
- yield queue.enqueue(1);
77
- const item = yield queue.dequeue();
75
+ await queue.enqueue(1);
76
+ const item = await queue.dequeue();
78
77
  queue.done();
79
78
  expect(item).toBe(1);
80
- }));
81
- it('dequeue after done does not hang', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
79
+ });
80
+ it('dequeue after done does not hang', async () => {
82
81
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
83
- yield queue.enqueue(1);
84
- const item = yield queue.dequeue();
82
+ await queue.enqueue(1);
83
+ const item = await queue.dequeue();
85
84
  queue.done();
86
- yield queue.dequeue();
85
+ await queue.dequeue();
87
86
  expect(item).toBe(1);
88
- }));
89
- it('dequeue after done and empty does not hang', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
87
+ });
88
+ it('dequeue after done and empty does not hang', async () => {
90
89
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
91
- yield queue.enqueue(1);
92
- const item = yield queue.dequeue();
90
+ await queue.enqueue(1);
91
+ const item = await queue.dequeue();
93
92
  queue.done();
94
- yield queue.dequeue();
93
+ await queue.dequeue();
95
94
  expect(item).toBe(1);
96
- yield queue.dequeue();
97
- }));
98
- it('enqueue after done throws', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
95
+ await queue.dequeue();
96
+ });
97
+ it('enqueue after done throws', async () => {
99
98
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
100
- yield queue.enqueue(1);
101
- const item = yield queue.dequeue();
99
+ await queue.enqueue(1);
100
+ const item = await queue.dequeue();
102
101
  queue.done();
103
- yield expect(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { return queue.enqueue(2); })).rejects.toThrowError('`enqueue` called after `done` called');
102
+ await expect(async () => queue.enqueue(2)).rejects.toThrowError('`enqueue` called after `done` called');
104
103
  expect(item).toBe(1);
105
- }));
106
- it('balanced enqueue/dequeue works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
104
+ });
105
+ it('balanced enqueue/dequeue works', async () => {
107
106
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
108
107
  const writers = [];
109
108
  writers.push(queue.enqueue(1));
@@ -111,13 +110,13 @@ describe('BlockingQueue', () => {
111
110
  const readers = [];
112
111
  readers.push(queue.dequeue());
113
112
  readers.push(queue.dequeue());
114
- yield Promise.all(writers);
113
+ await Promise.all(writers);
115
114
  queue.done();
116
- yield Promise.all(readers);
117
- expect(yield readers[0]).toBe(1);
118
- expect(yield readers[1]).toBe(2);
119
- }));
120
- it('more dequeue than enqueue works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
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 () => {
121
120
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
122
121
  const writers = [];
123
122
  writers.push(queue.enqueue(1));
@@ -127,49 +126,49 @@ describe('BlockingQueue', () => {
127
126
  readers.push(queue.dequeue());
128
127
  readers.push(queue.dequeue());
129
128
  readers.push(queue.dequeue());
130
- yield Promise.all(writers);
129
+ await Promise.all(writers);
131
130
  queue.done();
132
- yield Promise.all(readers);
133
- expect(yield readers[0]).toBe(1);
134
- expect(yield readers[1]).toBe(2);
135
- expect(yield readers[2]).toBeUndefined();
136
- expect(yield readers[3]).toBeUndefined();
137
- }));
138
- it('full queue blocks enqueue until dequeue', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
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 () => {
139
138
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 1 });
140
139
  // Wait for first item to add
141
- yield queue.enqueue(1);
140
+ await queue.enqueue(1);
142
141
  // Do not wait for second item to add (it will wait until an enqueue completes)
143
142
  setTimeout(() => {
144
143
  void queue.enqueue(2);
145
144
  }, 2000);
146
- expect(yield queue.dequeue()).toBe(1);
145
+ expect(await queue.dequeue()).toBe(1);
147
146
  const startTime = Date.now();
148
- expect(yield queue.dequeue()).toBe(2);
149
- const duration = Date.now() - startTime;
147
+ expect(await queue.dequeue()).toBe(2);
148
+ const duration = Math.ceil(Date.now() - startTime);
150
149
  expect(duration).toBeGreaterThanOrEqual(2000);
151
150
  queue.done();
152
- }));
151
+ });
153
152
  });
154
153
  describe('maxUnread: 2', () => {
155
- it('no reads until done works', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
154
+ it('no reads until done works', async () => {
156
155
  const queue = new blocking_queue_1.BlockingQueue({ maxUnread: 2 });
157
156
  const writers = [];
158
157
  writers.push(queue.enqueue(1));
159
158
  writers.push(queue.enqueue(2));
160
- yield Promise.all(writers);
159
+ await Promise.all(writers);
161
160
  queue.done();
162
161
  const readers = [];
163
162
  readers.push(queue.dequeue());
164
163
  readers.push(queue.dequeue());
165
164
  readers.push(queue.dequeue());
166
165
  readers.push(queue.dequeue());
167
- yield Promise.all(readers);
168
- expect(yield readers[0]).toBe(1);
169
- expect(yield readers[1]).toBe(2);
170
- expect(yield readers[2]).toBeUndefined();
171
- expect(yield readers[3]).toBeUndefined();
172
- }));
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
+ });
173
172
  });
174
173
  });
175
174
  //# sourceMappingURL=blocking-queue.test.js.map