@furystack/utils 8.2.0 → 8.2.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/CHANGELOG.md +7 -0
- package/esm/event-hub.spec.js +2 -0
- package/esm/event-hub.spec.js.map +1 -1
- package/esm/semaphore.spec.d.ts.map +1 -1
- package/esm/semaphore.spec.js +231 -230
- package/esm/semaphore.spec.js.map +1 -1
- package/package.json +1 -1
- package/src/event-hub.spec.ts +2 -0
- package/src/semaphore.spec.ts +255 -255
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [8.2.1] - 2026-03-06
|
|
4
|
+
|
|
5
|
+
### 🧪 Tests
|
|
6
|
+
|
|
7
|
+
- Refactored all `Semaphore` tests to use `using()` / `usingAsync()` wrappers for proper disposal, ensuring cleanup even if assertions fail
|
|
8
|
+
- Added `eslint-disable` comment in `EventHub` test for intentional post-disposal behavior verification
|
|
9
|
+
|
|
3
10
|
## [8.2.0] - 2026-03-03
|
|
4
11
|
|
|
5
12
|
### ✨ Features
|
package/esm/event-hub.spec.js
CHANGED
|
@@ -66,6 +66,8 @@ describe('EventHub', () => {
|
|
|
66
66
|
const hub = new EventHub();
|
|
67
67
|
const listener = vi.fn((_val) => { });
|
|
68
68
|
hub.addListener('test', listener);
|
|
69
|
+
// We want to test the dispose behavior, so we need to disable the rule
|
|
70
|
+
// eslint-disable-next-line furystack/prefer-using-wrapper
|
|
69
71
|
hub[Symbol.dispose]();
|
|
70
72
|
hub.emit('test', 'test');
|
|
71
73
|
expect(listener).not.toBeCalled();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-hub.spec.js","sourceRoot":"","sources":["../src/event-hub.spec.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"event-hub.spec.js","sourceRoot":"","sources":["../src/event-hub.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semaphore.spec.d.ts","sourceRoot":"","sources":["../src/semaphore.spec.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"semaphore.spec.d.ts","sourceRoot":"","sources":["../src/semaphore.spec.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,yCA4czB,CAAA"}
|
package/esm/semaphore.spec.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { sleepAsync } from './sleep-async.js';
|
|
3
3
|
import { using } from './using.js';
|
|
4
|
+
import { usingAsync } from './using-async.js';
|
|
4
5
|
import { Semaphore, SemaphoreDisposedError } from './semaphore.js';
|
|
5
6
|
export const semaphoreTests = describe('Semaphore', () => {
|
|
6
7
|
it('should be constructed with a given concurrency limit', () => {
|
|
@@ -14,284 +15,284 @@ export const semaphoreTests = describe('Semaphore', () => {
|
|
|
14
15
|
});
|
|
15
16
|
});
|
|
16
17
|
it('should execute a single task and return its result', async () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
await usingAsync(new Semaphore(2), async (s) => {
|
|
19
|
+
const result = await s.execute(async () => 42);
|
|
20
|
+
expect(result).toBe(42);
|
|
21
|
+
expect(s.completedCount.getValue()).toBe(1);
|
|
22
|
+
expect(s.runningCount.getValue()).toBe(0);
|
|
23
|
+
});
|
|
23
24
|
});
|
|
24
25
|
it('should execute up to N tasks concurrently and queue the rest', async () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
await usingAsync(new Semaphore(2), async (s) => {
|
|
27
|
+
const running = [];
|
|
28
|
+
const resolvers = [];
|
|
29
|
+
const createTask = (name) => s.execute(async () => {
|
|
30
|
+
running.push(name);
|
|
31
|
+
await new Promise((resolve) => resolvers.push(resolve));
|
|
32
|
+
return name;
|
|
33
|
+
});
|
|
34
|
+
const p1 = createTask('a');
|
|
35
|
+
const p2 = createTask('b');
|
|
36
|
+
const p3 = createTask('c');
|
|
37
|
+
await sleepAsync(10);
|
|
38
|
+
expect(running).toEqual(['a', 'b']);
|
|
39
|
+
expect(s.runningCount.getValue()).toBe(2);
|
|
40
|
+
expect(s.pendingCount.getValue()).toBe(1);
|
|
41
|
+
resolvers[0]();
|
|
42
|
+
await p1;
|
|
43
|
+
await sleepAsync(10);
|
|
44
|
+
expect(running).toEqual(['a', 'b', 'c']);
|
|
45
|
+
expect(s.runningCount.getValue()).toBe(2);
|
|
46
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
47
|
+
resolvers[1]();
|
|
48
|
+
resolvers[2]();
|
|
49
|
+
await Promise.all([p2, p3]);
|
|
50
|
+
expect(s.completedCount.getValue()).toBe(3);
|
|
51
|
+
expect(s.runningCount.getValue()).toBe(0);
|
|
32
52
|
});
|
|
33
|
-
const p1 = createTask('a');
|
|
34
|
-
const p2 = createTask('b');
|
|
35
|
-
const p3 = createTask('c');
|
|
36
|
-
await sleepAsync(10);
|
|
37
|
-
expect(running).toEqual(['a', 'b']);
|
|
38
|
-
expect(s.runningCount.getValue()).toBe(2);
|
|
39
|
-
expect(s.pendingCount.getValue()).toBe(1);
|
|
40
|
-
resolvers[0]();
|
|
41
|
-
await p1;
|
|
42
|
-
await sleepAsync(10);
|
|
43
|
-
expect(running).toEqual(['a', 'b', 'c']);
|
|
44
|
-
expect(s.runningCount.getValue()).toBe(2);
|
|
45
|
-
expect(s.pendingCount.getValue()).toBe(0);
|
|
46
|
-
resolvers[1]();
|
|
47
|
-
resolvers[2]();
|
|
48
|
-
await Promise.all([p2, p3]);
|
|
49
|
-
expect(s.completedCount.getValue()).toBe(3);
|
|
50
|
-
expect(s.runningCount.getValue()).toBe(0);
|
|
51
|
-
s[Symbol.dispose]();
|
|
52
53
|
});
|
|
53
54
|
it('should propagate task rejection to the caller and continue processing', async () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
56
|
+
const taskError = new Error('task failed');
|
|
57
|
+
const p1 = s.execute(async () => {
|
|
58
|
+
throw taskError;
|
|
59
|
+
});
|
|
60
|
+
const p2 = s.execute(async () => 'ok');
|
|
61
|
+
await expect(p1).rejects.toThrow('task failed');
|
|
62
|
+
const result = await p2;
|
|
63
|
+
expect(result).toBe('ok');
|
|
64
|
+
expect(s.failedCount.getValue()).toBe(1);
|
|
65
|
+
expect(s.completedCount.getValue()).toBe(1);
|
|
58
66
|
});
|
|
59
|
-
const p2 = s.execute(async () => 'ok');
|
|
60
|
-
await expect(p1).rejects.toThrow('task failed');
|
|
61
|
-
const result = await p2;
|
|
62
|
-
expect(result).toBe('ok');
|
|
63
|
-
expect(s.failedCount.getValue()).toBe(1);
|
|
64
|
-
expect(s.completedCount.getValue()).toBe(1);
|
|
65
|
-
s[Symbol.dispose]();
|
|
66
67
|
});
|
|
67
68
|
describe('ObservableValue counters', () => {
|
|
68
69
|
it('should update pendingCount and runningCount on transitions', async () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
71
|
+
const pendingChanges = [];
|
|
72
|
+
const runningChanges = [];
|
|
73
|
+
s.pendingCount.subscribe((v) => {
|
|
74
|
+
pendingChanges.push(v);
|
|
75
|
+
});
|
|
76
|
+
s.runningCount.subscribe((v) => {
|
|
77
|
+
runningChanges.push(v);
|
|
78
|
+
});
|
|
79
|
+
let resolve;
|
|
80
|
+
const p1 = s.execute(async () => {
|
|
81
|
+
await new Promise((r) => (resolve = r));
|
|
82
|
+
});
|
|
83
|
+
const p2 = s.execute(async () => 'done');
|
|
84
|
+
await sleepAsync(10);
|
|
85
|
+
expect(pendingChanges).toContain(1);
|
|
86
|
+
expect(runningChanges).toContain(1);
|
|
87
|
+
resolve();
|
|
88
|
+
await p1;
|
|
89
|
+
await sleepAsync(10);
|
|
90
|
+
await p2;
|
|
91
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
92
|
+
expect(s.runningCount.getValue()).toBe(0);
|
|
93
|
+
expect(s.completedCount.getValue()).toBe(2);
|
|
81
94
|
});
|
|
82
|
-
const p2 = s.execute(async () => 'done');
|
|
83
|
-
await sleepAsync(10);
|
|
84
|
-
expect(pendingChanges).toContain(1);
|
|
85
|
-
expect(runningChanges).toContain(1);
|
|
86
|
-
resolve();
|
|
87
|
-
await p1;
|
|
88
|
-
await sleepAsync(10);
|
|
89
|
-
await p2;
|
|
90
|
-
expect(s.pendingCount.getValue()).toBe(0);
|
|
91
|
-
expect(s.runningCount.getValue()).toBe(0);
|
|
92
|
-
expect(s.completedCount.getValue()).toBe(2);
|
|
93
|
-
s[Symbol.dispose]();
|
|
94
95
|
});
|
|
95
96
|
it('should update completedCount and failedCount correctly', async () => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
await usingAsync(new Semaphore(2), async (s) => {
|
|
98
|
+
await s.execute(async () => 'ok');
|
|
99
|
+
await s
|
|
100
|
+
.execute(async () => {
|
|
101
|
+
throw new Error('fail');
|
|
102
|
+
})
|
|
103
|
+
.catch(() => { });
|
|
104
|
+
expect(s.completedCount.getValue()).toBe(1);
|
|
105
|
+
expect(s.failedCount.getValue()).toBe(1);
|
|
106
|
+
});
|
|
106
107
|
});
|
|
107
108
|
});
|
|
108
109
|
describe('AbortSignal support', () => {
|
|
109
110
|
it('should abort a pending task when the caller signal aborts', async () => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
112
|
+
let resolve;
|
|
113
|
+
const p1 = s.execute(async () => {
|
|
114
|
+
await new Promise((r) => (resolve = r));
|
|
115
|
+
});
|
|
116
|
+
const controller = new AbortController();
|
|
117
|
+
const p2 = s.execute(async () => 'should not run', { signal: controller.signal });
|
|
118
|
+
await sleepAsync(10);
|
|
119
|
+
expect(s.pendingCount.getValue()).toBe(1);
|
|
120
|
+
controller.abort(new Error('cancelled'));
|
|
121
|
+
await expect(p2).rejects.toThrow('cancelled');
|
|
122
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
123
|
+
resolve();
|
|
124
|
+
await p1;
|
|
114
125
|
});
|
|
115
|
-
const controller = new AbortController();
|
|
116
|
-
const p2 = s.execute(async () => 'should not run', { signal: controller.signal });
|
|
117
|
-
await sleepAsync(10);
|
|
118
|
-
expect(s.pendingCount.getValue()).toBe(1);
|
|
119
|
-
controller.abort(new Error('cancelled'));
|
|
120
|
-
await expect(p2).rejects.toThrow('cancelled');
|
|
121
|
-
expect(s.pendingCount.getValue()).toBe(0);
|
|
122
|
-
resolve();
|
|
123
|
-
await p1;
|
|
124
|
-
s[Symbol.dispose]();
|
|
125
126
|
});
|
|
126
127
|
it('should reject immediately if the caller signal is already aborted', async () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
129
|
+
const controller = new AbortController();
|
|
130
|
+
controller.abort(new Error('pre-aborted'));
|
|
131
|
+
await expect(s.execute(async () => 'should not run', { signal: controller.signal })).rejects.toThrow('pre-aborted');
|
|
132
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
133
|
+
expect(s.runningCount.getValue()).toBe(0);
|
|
134
|
+
});
|
|
134
135
|
});
|
|
135
136
|
it('should clean up the caller signal listener when the task completes normally', async () => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
138
|
+
const controller = new AbortController();
|
|
139
|
+
const removeSpy = vi.spyOn(controller.signal, 'removeEventListener');
|
|
140
|
+
await s.execute(async () => 'done', { signal: controller.signal });
|
|
141
|
+
expect(removeSpy).toHaveBeenCalledWith('abort', expect.any(Function));
|
|
142
|
+
removeSpy.mockRestore();
|
|
143
|
+
});
|
|
143
144
|
});
|
|
144
145
|
it('should abort the signal passed to a running task when the caller signal aborts', async () => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
146
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
147
|
+
const signalAborted = vi.fn();
|
|
148
|
+
const controller = new AbortController();
|
|
149
|
+
const p = s.execute(async ({ signal }) => {
|
|
150
|
+
signal.addEventListener('abort', signalAborted);
|
|
151
|
+
await new Promise((resolve) => {
|
|
152
|
+
signal.addEventListener('abort', () => resolve());
|
|
153
|
+
});
|
|
154
|
+
throw signal.reason;
|
|
155
|
+
}, { signal: controller.signal });
|
|
156
|
+
await sleepAsync(10);
|
|
157
|
+
expect(s.runningCount.getValue()).toBe(1);
|
|
158
|
+
controller.abort(new Error('stop'));
|
|
159
|
+
await expect(p).rejects.toThrow('stop');
|
|
160
|
+
expect(signalAborted).toBeCalledTimes(1);
|
|
161
|
+
});
|
|
161
162
|
});
|
|
162
163
|
});
|
|
163
164
|
describe('EventHub events', () => {
|
|
164
165
|
it('should emit taskStarted when a task begins running', async () => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
167
|
+
const listener = vi.fn();
|
|
168
|
+
s.subscribe('taskStarted', listener);
|
|
169
|
+
await s.execute(async () => 'done');
|
|
170
|
+
expect(listener).toBeCalledTimes(1);
|
|
171
|
+
});
|
|
171
172
|
});
|
|
172
173
|
it('should emit taskCompleted when a task resolves', async () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
175
|
+
const listener = vi.fn();
|
|
176
|
+
s.subscribe('taskCompleted', listener);
|
|
177
|
+
await s.execute(async () => 'done');
|
|
178
|
+
expect(listener).toBeCalledTimes(1);
|
|
179
|
+
});
|
|
179
180
|
});
|
|
180
181
|
it('should emit taskFailed with the error when a task rejects', async () => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
182
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
183
|
+
const listener = vi.fn();
|
|
184
|
+
s.subscribe('taskFailed', listener);
|
|
185
|
+
const taskError = new Error('boom');
|
|
186
|
+
await s
|
|
187
|
+
.execute(async () => {
|
|
188
|
+
throw taskError;
|
|
189
|
+
})
|
|
190
|
+
.catch(() => { });
|
|
191
|
+
expect(listener).toBeCalledTimes(1);
|
|
192
|
+
expect(listener).toBeCalledWith({ error: taskError });
|
|
193
|
+
});
|
|
193
194
|
});
|
|
194
195
|
it('should emit events in correct order for queued tasks', async () => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
197
|
+
const events = [];
|
|
198
|
+
s.subscribe('taskStarted', () => {
|
|
199
|
+
events.push('started');
|
|
200
|
+
});
|
|
201
|
+
s.subscribe('taskCompleted', () => {
|
|
202
|
+
events.push('completed');
|
|
203
|
+
});
|
|
204
|
+
await Promise.all([s.execute(async () => 'a'), s.execute(async () => 'b')]);
|
|
205
|
+
await sleepAsync(10);
|
|
206
|
+
expect(events).toEqual(['started', 'completed', 'started', 'completed']);
|
|
202
207
|
});
|
|
203
|
-
await Promise.all([s.execute(async () => 'a'), s.execute(async () => 'b')]);
|
|
204
|
-
await sleepAsync(10);
|
|
205
|
-
expect(events).toEqual(['started', 'completed', 'started', 'completed']);
|
|
206
|
-
s[Symbol.dispose]();
|
|
207
208
|
});
|
|
208
209
|
});
|
|
209
210
|
describe('setMaxConcurrent', () => {
|
|
210
211
|
it('should return the updated value from getMaxConcurrent', () => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
212
|
+
using(new Semaphore(2), (s) => {
|
|
213
|
+
s.setMaxConcurrent(5);
|
|
214
|
+
expect(s.getMaxConcurrent()).toBe(5);
|
|
215
|
+
});
|
|
215
216
|
});
|
|
216
217
|
it('should throw when given a non-positive integer', () => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
using(new Semaphore(2), (s) => {
|
|
219
|
+
expect(() => s.setMaxConcurrent(0)).toThrow('maxConcurrent must be a positive integer');
|
|
220
|
+
expect(() => s.setMaxConcurrent(-1)).toThrow('maxConcurrent must be a positive integer');
|
|
221
|
+
expect(() => s.setMaxConcurrent(1.5)).toThrow('maxConcurrent must be a positive integer');
|
|
222
|
+
});
|
|
222
223
|
});
|
|
223
224
|
it('should immediately start queued tasks when increased', async () => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
await usingAsync(new Semaphore(1), async (s) => {
|
|
226
|
+
const running = [];
|
|
227
|
+
const resolvers = [];
|
|
228
|
+
const createTask = (name) => s.execute(async () => {
|
|
229
|
+
running.push(name);
|
|
230
|
+
await new Promise((resolve) => resolvers.push(resolve));
|
|
231
|
+
return name;
|
|
232
|
+
});
|
|
233
|
+
const p1 = createTask('a');
|
|
234
|
+
const p2 = createTask('b');
|
|
235
|
+
const p3 = createTask('c');
|
|
236
|
+
await sleepAsync(10);
|
|
237
|
+
expect(running).toEqual(['a']);
|
|
238
|
+
expect(s.runningCount.getValue()).toBe(1);
|
|
239
|
+
expect(s.pendingCount.getValue()).toBe(2);
|
|
240
|
+
s.setMaxConcurrent(3);
|
|
241
|
+
await sleepAsync(10);
|
|
242
|
+
expect(running).toEqual(['a', 'b', 'c']);
|
|
243
|
+
expect(s.runningCount.getValue()).toBe(3);
|
|
244
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
245
|
+
resolvers.forEach((r) => r());
|
|
246
|
+
await Promise.all([p1, p2, p3]);
|
|
231
247
|
});
|
|
232
|
-
const p1 = createTask('a');
|
|
233
|
-
const p2 = createTask('b');
|
|
234
|
-
const p3 = createTask('c');
|
|
235
|
-
await sleepAsync(10);
|
|
236
|
-
expect(running).toEqual(['a']);
|
|
237
|
-
expect(s.runningCount.getValue()).toBe(1);
|
|
238
|
-
expect(s.pendingCount.getValue()).toBe(2);
|
|
239
|
-
s.setMaxConcurrent(3);
|
|
240
|
-
await sleepAsync(10);
|
|
241
|
-
expect(running).toEqual(['a', 'b', 'c']);
|
|
242
|
-
expect(s.runningCount.getValue()).toBe(3);
|
|
243
|
-
expect(s.pendingCount.getValue()).toBe(0);
|
|
244
|
-
resolvers.forEach((r) => r());
|
|
245
|
-
await Promise.all([p1, p2, p3]);
|
|
246
|
-
s[Symbol.dispose]();
|
|
247
248
|
});
|
|
248
249
|
it('should not abort running tasks when decreased', async () => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
await usingAsync(new Semaphore(3), async (s) => {
|
|
251
|
+
const resolvers = [];
|
|
252
|
+
const createTask = () => s.execute(async () => {
|
|
253
|
+
await new Promise((resolve) => resolvers.push(resolve));
|
|
254
|
+
});
|
|
255
|
+
const p1 = createTask();
|
|
256
|
+
const p2 = createTask();
|
|
257
|
+
const p3 = createTask();
|
|
258
|
+
await sleepAsync(10);
|
|
259
|
+
expect(s.runningCount.getValue()).toBe(3);
|
|
260
|
+
s.setMaxConcurrent(1);
|
|
261
|
+
expect(s.runningCount.getValue()).toBe(3);
|
|
262
|
+
resolvers.forEach((r) => r());
|
|
263
|
+
await Promise.all([p1, p2, p3]);
|
|
264
|
+
expect(s.completedCount.getValue()).toBe(3);
|
|
253
265
|
});
|
|
254
|
-
const p1 = createTask();
|
|
255
|
-
const p2 = createTask();
|
|
256
|
-
const p3 = createTask();
|
|
257
|
-
await sleepAsync(10);
|
|
258
|
-
expect(s.runningCount.getValue()).toBe(3);
|
|
259
|
-
s.setMaxConcurrent(1);
|
|
260
|
-
expect(s.runningCount.getValue()).toBe(3);
|
|
261
|
-
resolvers.forEach((r) => r());
|
|
262
|
-
await Promise.all([p1, p2, p3]);
|
|
263
|
-
expect(s.completedCount.getValue()).toBe(3);
|
|
264
|
-
s[Symbol.dispose]();
|
|
265
266
|
});
|
|
266
267
|
it('should not start new tasks until running count drops below new lower limit', async () => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
await usingAsync(new Semaphore(2), async (s) => {
|
|
269
|
+
const running = [];
|
|
270
|
+
const resolvers = [];
|
|
271
|
+
const createTask = (name) => s.execute(async () => {
|
|
272
|
+
running.push(name);
|
|
273
|
+
await new Promise((resolve) => resolvers.push(resolve));
|
|
274
|
+
return name;
|
|
275
|
+
});
|
|
276
|
+
const p1 = createTask('a');
|
|
277
|
+
const p2 = createTask('b');
|
|
278
|
+
const p3 = createTask('c');
|
|
279
|
+
await sleepAsync(10);
|
|
280
|
+
expect(running).toEqual(['a', 'b']);
|
|
281
|
+
expect(s.pendingCount.getValue()).toBe(1);
|
|
282
|
+
s.setMaxConcurrent(1);
|
|
283
|
+
resolvers[0]();
|
|
284
|
+
await p1;
|
|
285
|
+
await sleepAsync(10);
|
|
286
|
+
expect(running).toEqual(['a', 'b']);
|
|
287
|
+
expect(s.pendingCount.getValue()).toBe(1);
|
|
288
|
+
resolvers[1]();
|
|
289
|
+
await p2;
|
|
290
|
+
await sleepAsync(10);
|
|
291
|
+
expect(running).toEqual(['a', 'b', 'c']);
|
|
292
|
+
expect(s.pendingCount.getValue()).toBe(0);
|
|
293
|
+
resolvers[2]();
|
|
294
|
+
await p3;
|
|
274
295
|
});
|
|
275
|
-
const p1 = createTask('a');
|
|
276
|
-
const p2 = createTask('b');
|
|
277
|
-
const p3 = createTask('c');
|
|
278
|
-
await sleepAsync(10);
|
|
279
|
-
expect(running).toEqual(['a', 'b']);
|
|
280
|
-
expect(s.pendingCount.getValue()).toBe(1);
|
|
281
|
-
s.setMaxConcurrent(1);
|
|
282
|
-
resolvers[0]();
|
|
283
|
-
await p1;
|
|
284
|
-
await sleepAsync(10);
|
|
285
|
-
expect(running).toEqual(['a', 'b']);
|
|
286
|
-
expect(s.pendingCount.getValue()).toBe(1);
|
|
287
|
-
resolvers[1]();
|
|
288
|
-
await p2;
|
|
289
|
-
await sleepAsync(10);
|
|
290
|
-
expect(running).toEqual(['a', 'b', 'c']);
|
|
291
|
-
expect(s.pendingCount.getValue()).toBe(0);
|
|
292
|
-
resolvers[2]();
|
|
293
|
-
await p3;
|
|
294
|
-
s[Symbol.dispose]();
|
|
295
296
|
});
|
|
296
297
|
});
|
|
297
298
|
describe('Disposal', () => {
|
|
@@ -348,7 +349,7 @@ export const semaphoreTests = describe('Semaphore', () => {
|
|
|
348
349
|
const listener = vi.fn();
|
|
349
350
|
s.subscribe('taskStarted', listener);
|
|
350
351
|
s[Symbol.dispose]();
|
|
351
|
-
s.emit('taskStarted', undefined);
|
|
352
|
+
expect(() => s.emit('taskStarted', undefined)).not.toThrow();
|
|
352
353
|
expect(listener).not.toBeCalled();
|
|
353
354
|
});
|
|
354
355
|
});
|