@xylabs/threads 3.6.6 → 3.6.8
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/dist/esm/master/implementation.browser.js +3 -3
- package/dist/esm/master/implementation.node.js +11 -9
- package/dist/esm/master/pool.js +9 -9
- package/dist/esm/master/spawn.js +4 -4
- package/dist/esm/observable-promise.js +2 -2
- package/dist/esm/observable.js +1 -1
- package/dist/esm/worker/index.js +2 -2
- package/dist/master/implementation.browser.js +3 -3
- package/dist/master/implementation.node.js +11 -9
- package/dist/master/pool.js +9 -9
- package/dist/master/spawn.js +4 -4
- package/dist/observable-promise.js +2 -2
- package/dist/observable.js +1 -1
- package/dist/worker/index.js +2 -2
- package/observable.d.ts +1 -0
- package/package.json +6 -6
- package/register.d.ts +1 -0
- package/rollup.config.js +0 -1
- package/src/index.ts +1 -0
- package/src/master/implementation.browser.ts +4 -3
- package/src/master/implementation.node.ts +20 -17
- package/src/master/index.ts +1 -0
- package/src/master/invocation-proxy.ts +1 -1
- package/src/master/pool-types.ts +28 -28
- package/src/master/pool.ts +10 -10
- package/src/master/spawn.ts +10 -9
- package/src/master/thread.ts +1 -0
- package/src/observable-promise.ts +11 -11
- package/src/observable.ts +1 -2
- package/src/ponyfills.ts +6 -6
- package/src/serializers.ts +1 -0
- package/src/transferable.ts +0 -1
- package/src/types/master.ts +4 -3
- package/src/worker/implementation.browser.ts +1 -0
- package/src/worker/implementation.tiny-worker.ts +1 -1
- package/src/worker/implementation.ts +1 -0
- package/src/worker/implementation.worker_threads.ts +1 -0
- package/src/worker/index.ts +3 -2
- package/test/lib/serialization.ts +2 -2
- package/test/observable-promise.test.ts +9 -9
- package/test/observable.test.ts +7 -6
- package/test/pool.test.ts +7 -7
- package/test/serialization.test.ts +1 -0
- package/test/spawn.chromium.mocha.ts +1 -0
- package/test/spawn.test.ts +4 -2
- package/test/streaming.test.ts +1 -1
- package/test/transferables.test.ts +3 -2
- package/test/workers/minmax.ts +1 -0
- package/test/workers/serialization.ts +1 -0
- package/test-tooling/webpack/app.ts +4 -5
- package/test-tooling/webpack/webpack.chromium.mocha.ts +1 -1
- package/test-tooling/webpack/webpack.node.config.js +0 -1
- package/test-tooling/webpack/webpack.test.ts +13 -7
- package/test-tooling/webpack/webpack.web.config.js +0 -1
- package/worker.d.ts +1 -0
package/src/master/pool-types.ts
CHANGED
|
@@ -19,41 +19,41 @@ export type TaskRunFunction<ThreadType extends Thread, Return> = (worker: Thread
|
|
|
19
19
|
/** Pool event. Subscribe to those events using `pool.events()`. Useful for debugging. */
|
|
20
20
|
export type PoolEvent<ThreadType extends Thread> =
|
|
21
21
|
| {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
type: PoolEventType.initialized
|
|
23
|
+
size: number
|
|
24
|
+
}
|
|
25
25
|
| {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
type: PoolEventType.taskQueued
|
|
27
|
+
taskID: number
|
|
28
|
+
}
|
|
29
29
|
| {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
type: PoolEventType.taskQueueDrained
|
|
31
|
+
}
|
|
32
32
|
| {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
type: PoolEventType.taskStart
|
|
34
|
+
taskID: number
|
|
35
|
+
workerID: number
|
|
36
|
+
}
|
|
37
37
|
| {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
type: PoolEventType.taskCompleted
|
|
39
|
+
returnValue: any
|
|
40
|
+
taskID: number
|
|
41
|
+
workerID: number
|
|
42
|
+
}
|
|
43
43
|
| {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
type: PoolEventType.taskFailed
|
|
45
|
+
error: Error
|
|
46
|
+
taskID: number
|
|
47
|
+
workerID: number
|
|
48
|
+
}
|
|
49
49
|
| {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
type: PoolEventType.taskCanceled
|
|
51
|
+
taskID: number
|
|
52
|
+
}
|
|
53
53
|
| {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
type: PoolEventType.terminated
|
|
55
|
+
remainingQueue: Array<QueuedTask<ThreadType, any>>
|
|
56
|
+
}
|
|
57
57
|
|
|
58
58
|
export interface WorkerDescriptor<ThreadType extends Thread> {
|
|
59
59
|
init: Promise<ThreadType>
|
package/src/master/pool.ts
CHANGED
|
@@ -31,7 +31,7 @@ function createArray(size: number): number[] {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function delay(ms: number) {
|
|
34
|
-
return new Promise(
|
|
34
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function flatMap<In, Out>(array: In[], mapper: (element: In) => Out[]): Out[] {
|
|
@@ -133,7 +133,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
133
133
|
|
|
134
134
|
this.eventObservable = multicast(Observable.from(this.eventSubject))
|
|
135
135
|
|
|
136
|
-
Promise.all(this.workers.map(
|
|
136
|
+
Promise.all(this.workers.map(worker => worker.init)).then(
|
|
137
137
|
() =>
|
|
138
138
|
this.eventSubject.next({
|
|
139
139
|
size: this.workers.length,
|
|
@@ -149,7 +149,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
149
149
|
|
|
150
150
|
private findIdlingWorker(): WorkerDescriptor<ThreadType> | undefined {
|
|
151
151
|
const { concurrency = 1 } = this.options
|
|
152
|
-
return this.workers.find(
|
|
152
|
+
return this.workers.find(worker => worker.runningTasks.length < concurrency)
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
private async runPoolTask(worker: WorkerDescriptor<ThreadType>, task: QueuedTask<ThreadType, any>) {
|
|
@@ -186,7 +186,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
186
186
|
private async run(worker: WorkerDescriptor<ThreadType>, task: QueuedTask<ThreadType, any>) {
|
|
187
187
|
const runPromise = (async () => {
|
|
188
188
|
const removeTaskFromWorkersRunningTasks = () => {
|
|
189
|
-
worker.runningTasks = worker.runningTasks.filter(
|
|
189
|
+
worker.runningTasks = worker.runningTasks.filter(someRunPromise => someRunPromise !== runPromise)
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
// Defer task execution by one tick to give handlers time to subscribe
|
|
@@ -240,7 +240,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
async settled(allowResolvingImmediately: boolean = false): Promise<Error[]> {
|
|
243
|
-
const getCurrentlyRunningTasks = () => flatMap(this.workers,
|
|
243
|
+
const getCurrentlyRunningTasks = () => flatMap(this.workers, worker => worker.runningTasks)
|
|
244
244
|
|
|
245
245
|
const taskFailures: Error[] = []
|
|
246
246
|
|
|
@@ -327,7 +327,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
327
327
|
const task: QueuedTask<ThreadType, any> = {
|
|
328
328
|
cancel: () => {
|
|
329
329
|
if (!this.taskQueue.includes(task)) return
|
|
330
|
-
this.taskQueue = this.taskQueue.filter(
|
|
330
|
+
this.taskQueue = this.taskQueue.filter(someTask => someTask !== task)
|
|
331
331
|
this.eventSubject.next({
|
|
332
332
|
taskID: task.id,
|
|
333
333
|
type: PoolEventType.taskCanceled,
|
|
@@ -340,9 +340,9 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
340
340
|
|
|
341
341
|
if (this.taskQueue.length >= maxQueuedJobs) {
|
|
342
342
|
throw new Error(
|
|
343
|
-
'Maximum number of pool tasks queued. Refusing to queue another one.\n'
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
'Maximum number of pool tasks queued. Refusing to queue another one.\n'
|
|
344
|
+
+ 'This usually happens for one of two reasons: We are either at peak '
|
|
345
|
+
+ "workload right now or some tasks just won't finish, thus blocking the pool.",
|
|
346
346
|
)
|
|
347
347
|
}
|
|
348
348
|
|
|
@@ -368,7 +368,7 @@ class WorkerPool<ThreadType extends Thread> implements Pool<ThreadType> {
|
|
|
368
368
|
type: PoolEventType.terminated,
|
|
369
369
|
})
|
|
370
370
|
this.eventSubject.complete()
|
|
371
|
-
await Promise.all(this.workers.map(async
|
|
371
|
+
await Promise.all(this.workers.map(async worker => Thread.terminate(await worker.init)))
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
|
package/src/master/spawn.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
1
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
3
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
3
4
|
import DebugLogger from 'debug'
|
|
@@ -27,9 +28,9 @@ type ArbitraryThreadType = FunctionThread<any, any> & ModuleThread<any>
|
|
|
27
28
|
|
|
28
29
|
export type ExposedToThreadType<Exposed extends WorkerFunction | WorkerModule<any>> =
|
|
29
30
|
Exposed extends ArbitraryWorkerInterface ? ArbitraryThreadType
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
: Exposed extends WorkerFunction ? FunctionThread<Parameters<Exposed>, StripAsync<ReturnType<Exposed>>>
|
|
32
|
+
: Exposed extends WorkerModule<any> ? ModuleThread<Exposed>
|
|
33
|
+
: never
|
|
33
34
|
|
|
34
35
|
const debugMessages = DebugLogger('threads:master:messages')
|
|
35
36
|
const debugSpawn = DebugLogger('threads:master:spawn')
|
|
@@ -38,10 +39,10 @@ const debugThreadUtils = DebugLogger('threads:master:thread-utils')
|
|
|
38
39
|
const isInitMessage = (data: any): data is WorkerInitMessage => data && data.type === ('init' as const)
|
|
39
40
|
const isUncaughtErrorMessage = (data: any): data is WorkerUncaughtErrorMessage => data && data.type === ('uncaughtError' as const)
|
|
40
41
|
|
|
41
|
-
const initMessageTimeout
|
|
42
|
-
typeof process !== 'undefined' && process.env !== undefined && process.env.THREADS_WORKER_INIT_TIMEOUT
|
|
43
|
-
Number.parseInt(process.env.THREADS_WORKER_INIT_TIMEOUT, 10)
|
|
44
|
-
|
|
42
|
+
const initMessageTimeout
|
|
43
|
+
= typeof process !== 'undefined' && process.env !== undefined && process.env.THREADS_WORKER_INIT_TIMEOUT
|
|
44
|
+
? Number.parseInt(process.env.THREADS_WORKER_INIT_TIMEOUT, 10)
|
|
45
|
+
: 10_000
|
|
45
46
|
|
|
46
47
|
async function withTimeout<T>(promise: Promise<T>, timeoutInMs: number, errorMessage: string): Promise<T> {
|
|
47
48
|
let timeoutHandle: any
|
|
@@ -121,8 +122,8 @@ function setPrivateThreadProps<T>(
|
|
|
121
122
|
terminate: () => Promise<void>,
|
|
122
123
|
): T & PrivateThreadProps {
|
|
123
124
|
const workerErrors = workerEvents
|
|
124
|
-
.filter(
|
|
125
|
-
.map(
|
|
125
|
+
.filter(event => event.type === WorkerEventType.internalError)
|
|
126
|
+
.map(errorEvent => (errorEvent as WorkerInternalErrorEvent).error)
|
|
126
127
|
|
|
127
128
|
return Object.assign(raw as any, {
|
|
128
129
|
[$errors]: workerErrors,
|
package/src/master/thread.ts
CHANGED
|
@@ -166,17 +166,17 @@ export class ObservablePromise<T> extends Observable<T> implements Promise<T> {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
static from<T>(thing: Observable<T> | ObservableLike<T> | ArrayLike<T> | Thenable<T>): ObservablePromise<T> {
|
|
169
|
-
return isThenable(thing)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
return isThenable(thing)
|
|
170
|
+
? new ObservablePromise((observer) => {
|
|
171
|
+
const onFulfilled = (value: T) => {
|
|
172
|
+
observer.next(value)
|
|
173
|
+
observer.complete()
|
|
174
|
+
}
|
|
175
|
+
const onRejected = (error: any) => {
|
|
176
|
+
observer.error(error)
|
|
177
|
+
}
|
|
178
|
+
thing.then(onFulfilled, onRejected)
|
|
179
|
+
})
|
|
180
180
|
: (super.from(thing) as ObservablePromise<T>)
|
|
181
181
|
}
|
|
182
182
|
}
|
package/src/observable.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable sonarjs/prefer-immediate-return */
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
2
|
import { Observable, ObservableLike, SubscriptionObserver } from 'observable-fns'
|
|
4
3
|
|
|
@@ -20,7 +19,7 @@ export class Subject<T> extends Observable<T> implements ObservableLike<T> {
|
|
|
20
19
|
super((observer) => {
|
|
21
20
|
this[$observers] = [...(this[$observers] || []), observer]
|
|
22
21
|
const unsubscribe = () => {
|
|
23
|
-
this[$observers] = this[$observers].filter(
|
|
22
|
+
this[$observers] = this[$observers].filter(someObserver => someObserver !== observer)
|
|
24
23
|
}
|
|
25
24
|
return unsubscribe
|
|
26
25
|
})
|
package/src/ponyfills.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
export type SettlementResult<T> =
|
|
3
3
|
| {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
status: 'fulfilled'
|
|
5
|
+
value: T
|
|
6
|
+
}
|
|
7
7
|
| {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
reason: any
|
|
9
|
+
status: 'rejected'
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
// Based on <https://github.com/es-shims/Promise.allSettled/blob/master/implementation.js>
|
|
13
13
|
export function allSettled<T>(values: T[]): Promise<Array<SettlementResult<T>>> {
|
package/src/serializers.ts
CHANGED
package/src/transferable.ts
CHANGED
package/src/types/master.ts
CHANGED
|
@@ -20,8 +20,8 @@ interface ObservableLike<T> {
|
|
|
20
20
|
|
|
21
21
|
export type StripAsync<Type> =
|
|
22
22
|
Type extends Promise<infer PromiseBaseType> ? PromiseBaseType
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
: Type extends ObservableLike<infer ObservableBaseType> ? ObservableBaseType
|
|
24
|
+
: Type
|
|
25
25
|
|
|
26
26
|
export type StripTransfer<Type> = Type extends TransferDescriptor<infer BaseType> ? BaseType : Type
|
|
27
27
|
|
|
@@ -32,7 +32,7 @@ export type ProxyableArgs<Args extends any[]> =
|
|
|
32
32
|
|
|
33
33
|
export type ProxyableFunction<Args extends any[], ReturnType> =
|
|
34
34
|
Args extends [] ? () => ObservablePromise<StripTransfer<StripAsync<ReturnType>>>
|
|
35
|
-
|
|
35
|
+
: (...args: ProxyableArgs<Args>) => ObservablePromise<StripTransfer<StripAsync<ReturnType>>>
|
|
36
36
|
|
|
37
37
|
export type ModuleProxy<Methods extends ModuleMethods> = {
|
|
38
38
|
[method in keyof Methods]: ProxyableFunction<Parameters<Methods[method]>, ReturnType<Methods[method]>>
|
|
@@ -55,6 +55,7 @@ interface AnyFunctionThread extends PrivateThreadProps {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// tslint:disable-next-line no-empty-interface
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
58
59
|
interface AnyModuleThread extends PrivateThreadProps {
|
|
59
60
|
// Not specifying an index signature here as that would make `ModuleThread` incompatible
|
|
60
61
|
}
|
package/src/worker/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
1
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
3
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
3
4
|
import isSomeObservable from 'is-observable-2-1-0'
|
|
@@ -130,7 +131,7 @@ async function runFunction(jobUID: number, fn: WorkerFunction, args: any[]) {
|
|
|
130
131
|
|
|
131
132
|
if (isObservable(syncResult)) {
|
|
132
133
|
const subscription = syncResult.subscribe(
|
|
133
|
-
|
|
134
|
+
value => postJobResultMessage(jobUID, false, serialize(value)),
|
|
134
135
|
(error) => {
|
|
135
136
|
postJobErrorMessage(jobUID, serialize(error) as any)
|
|
136
137
|
activeSubscriptions.delete(jobUID)
|
|
@@ -181,7 +182,7 @@ export function expose(exposed: WorkerFunction | WorkerModule<any>) {
|
|
|
181
182
|
}
|
|
182
183
|
})
|
|
183
184
|
|
|
184
|
-
const methodNames = Object.keys(exposed).filter(
|
|
185
|
+
const methodNames = Object.keys(exposed).filter(key => typeof exposed[key] === 'function')
|
|
185
186
|
postModuleInitMessage(methodNames)
|
|
186
187
|
} else {
|
|
187
188
|
throw new Error(`Invalid argument passed to expose(). Expected a function or an object, got: ${exposed}`)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/* eslint-disable
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
import test from 'ava'
|
|
4
4
|
import { Observable } from 'observable-fns'
|
|
5
5
|
|
|
6
6
|
import { ObservablePromise } from '../src/observable-promise'
|
|
7
7
|
|
|
8
|
-
const delay = (ms: number) => new Promise(
|
|
8
|
+
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
9
9
|
|
|
10
10
|
test('can create an observable promise', async (t) => {
|
|
11
11
|
t.plan(1)
|
|
@@ -42,9 +42,9 @@ test('can proxy a promise fulfillment', async (t) => {
|
|
|
42
42
|
}, 1)
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
const promise1 = async.then(
|
|
45
|
+
const promise1 = async.then(value => t.is(value, 123), t.fail)
|
|
46
46
|
await delay(10)
|
|
47
|
-
const promise2 = async.then(
|
|
47
|
+
const promise2 = async.then(value => t.is(value, 123), t.fail)
|
|
48
48
|
|
|
49
49
|
await Promise.all([promise1, promise2])
|
|
50
50
|
})
|
|
@@ -103,7 +103,7 @@ test('can subscribe to values and completion', async (t) => {
|
|
|
103
103
|
|
|
104
104
|
for (let index = 0; index < 2; index++) {
|
|
105
105
|
async.subscribe(
|
|
106
|
-
|
|
106
|
+
value => capturedValues.push(value),
|
|
107
107
|
() => {},
|
|
108
108
|
() => capturedCompletions++,
|
|
109
109
|
)
|
|
@@ -130,8 +130,8 @@ test('can subscribe to errors', async (t) => {
|
|
|
130
130
|
|
|
131
131
|
for (let index = 0; index < 2; index++) {
|
|
132
132
|
async.subscribe(
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
value => capturedValues.push(value),
|
|
134
|
+
error => capturedErrorMessages.push(error.message),
|
|
135
135
|
() => capturedCompletions++,
|
|
136
136
|
)
|
|
137
137
|
}
|
|
@@ -160,8 +160,8 @@ test('from(Observable) works', async (t) => {
|
|
|
160
160
|
|
|
161
161
|
for (let index = 0; index < 2; index++) {
|
|
162
162
|
async.subscribe(
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
value => capturedValues.push(value),
|
|
164
|
+
error => capturedErrorMessages.push(error.message),
|
|
165
165
|
() => capturedCompletions++,
|
|
166
166
|
)
|
|
167
167
|
}
|
package/test/observable.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
1
2
|
/* eslint-disable require-await */
|
|
2
3
|
|
|
3
4
|
import test from 'ava'
|
|
@@ -16,17 +17,17 @@ test('Observable subject emits values and completion event', async (t) => {
|
|
|
16
17
|
const observable = Observable.from(subject)
|
|
17
18
|
|
|
18
19
|
const subscription1 = subject.subscribe(
|
|
19
|
-
|
|
20
|
+
value => values1.push(value),
|
|
20
21
|
undefined,
|
|
21
22
|
() => (completed1 = true),
|
|
22
23
|
)
|
|
23
24
|
subject.subscribe(
|
|
24
|
-
|
|
25
|
+
value => values2.push(value),
|
|
25
26
|
undefined,
|
|
26
27
|
() => (completed2 = true),
|
|
27
28
|
)
|
|
28
29
|
observable.subscribe(
|
|
29
|
-
|
|
30
|
+
value => values3.push(value),
|
|
30
31
|
undefined,
|
|
31
32
|
() => (completed3 = true),
|
|
32
33
|
)
|
|
@@ -58,17 +59,17 @@ test('Observable subject propagates errors', async (t) => {
|
|
|
58
59
|
|
|
59
60
|
const subscription1 = subject.subscribe(
|
|
60
61
|
() => {},
|
|
61
|
-
|
|
62
|
+
error => (error1 = error),
|
|
62
63
|
() => (completed1 = true),
|
|
63
64
|
)
|
|
64
65
|
subject.subscribe(
|
|
65
66
|
() => {},
|
|
66
|
-
|
|
67
|
+
error => (error2 = error),
|
|
67
68
|
() => (completed2 = true),
|
|
68
69
|
)
|
|
69
70
|
observable.subscribe(
|
|
70
71
|
() => {},
|
|
71
|
-
|
|
72
|
+
error => (error3 = error),
|
|
72
73
|
() => (completed3 = true),
|
|
73
74
|
)
|
|
74
75
|
|
package/test/pool.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/* eslint-disable
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
2
|
+
|
|
2
3
|
/* eslint-disable require-await */
|
|
3
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
5
|
|
|
@@ -21,14 +22,14 @@ test.serial('thread pool basics work and events are emitted', async (t) => {
|
|
|
21
22
|
return spawn<() => string>(new Worker(workerPath))
|
|
22
23
|
}
|
|
23
24
|
const pool = Pool(spawnHelloWorld, 3)
|
|
24
|
-
pool.events().subscribe(
|
|
25
|
+
pool.events().subscribe(event => events.push(event))
|
|
25
26
|
|
|
26
27
|
// Just to make sure all worker threads are initialized before starting to queue
|
|
27
28
|
// This is only necessary for testing to make sure that this is the first event recorded
|
|
28
29
|
await new Promise((resolve, reject) => {
|
|
29
30
|
pool
|
|
30
31
|
.events()
|
|
31
|
-
.filter(
|
|
32
|
+
.filter(event => event.type === PoolEventType.initialized)
|
|
32
33
|
.subscribe(resolve, reject)
|
|
33
34
|
})
|
|
34
35
|
|
|
@@ -111,7 +112,6 @@ test.serial('pool.completed(true) works', async (t) => {
|
|
|
111
112
|
})
|
|
112
113
|
|
|
113
114
|
test.serial('pool.settled() does not reject on task failure', async (t) => {
|
|
114
|
-
// eslint-disable-next-line sonarjs/no-unused-collection
|
|
115
115
|
const returned: any[] = []
|
|
116
116
|
|
|
117
117
|
const spawnHelloWorld = () => spawn(new Worker(workerPath))
|
|
@@ -129,7 +129,7 @@ test.serial('pool.settled() does not reject on task failure', async (t) => {
|
|
|
129
129
|
|
|
130
130
|
const errors = await pool.settled()
|
|
131
131
|
t.is(errors.length, 2)
|
|
132
|
-
t.deepEqual(errors.map(
|
|
132
|
+
t.deepEqual(errors.map(error => error.message).sort(), ['Test error one', 'Test error two'])
|
|
133
133
|
})
|
|
134
134
|
|
|
135
135
|
test.serial('pool.settled(true) works', async (t) => {
|
|
@@ -145,7 +145,7 @@ test.serial('task.cancel() works', async (t) => {
|
|
|
145
145
|
const spawnHelloWorld = () => spawn(new Worker(workerPath))
|
|
146
146
|
const pool = Pool(spawnHelloWorld, 1)
|
|
147
147
|
|
|
148
|
-
pool.events().subscribe(
|
|
148
|
+
pool.events().subscribe(event => events.push(event))
|
|
149
149
|
|
|
150
150
|
let executionCount = 0
|
|
151
151
|
const tasks: QueuedTask<any, any>[] = []
|
|
@@ -164,7 +164,7 @@ test.serial('task.cancel() works', async (t) => {
|
|
|
164
164
|
await pool.completed()
|
|
165
165
|
t.is(executionCount, 2)
|
|
166
166
|
|
|
167
|
-
const cancellationEvents = events.filter(
|
|
167
|
+
const cancellationEvents = events.filter(event => event.type === 'taskCanceled')
|
|
168
168
|
t.deepEqual(cancellationEvents, [
|
|
169
169
|
{
|
|
170
170
|
taskID: 3,
|
package/test/spawn.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/* eslint-disable
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
2
|
+
|
|
2
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
import test from 'ava'
|
|
4
5
|
import { Observable } from 'observable-fns'
|
|
@@ -27,7 +28,7 @@ test('can subscribe to an observable returned by a thread call', async (t) => {
|
|
|
27
28
|
const encounteredValues: any[] = []
|
|
28
29
|
|
|
29
30
|
const observable = countToFive()
|
|
30
|
-
observable.subscribe(
|
|
31
|
+
observable.subscribe(value => encounteredValues.push(value))
|
|
31
32
|
await observable
|
|
32
33
|
|
|
33
34
|
t.deepEqual(encounteredValues, [1, 2, 3, 4, 5])
|
|
@@ -51,6 +52,7 @@ test('thread job errors are handled', async (t) => {
|
|
|
51
52
|
})
|
|
52
53
|
|
|
53
54
|
test('thread transfer errors are handled', async (t) => {
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
54
56
|
const builtin = require('node:module').builtinModules
|
|
55
57
|
if (builtin.includes('worker_threads')) {
|
|
56
58
|
// test is actual for native worker_threads only
|
package/test/streaming.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ test('can use worker returning an observable subject', async (t) => {
|
|
|
6
6
|
const captured: Array<{ max: number; min: number }> = []
|
|
7
7
|
|
|
8
8
|
const minmax = await spawn(new Worker('./workers/minmax'))
|
|
9
|
-
minmax.values().subscribe(
|
|
9
|
+
minmax.values().subscribe(values => captured.push(values))
|
|
10
10
|
|
|
11
11
|
await minmax.push(2)
|
|
12
12
|
await minmax.push(3)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
1
2
|
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
|
|
2
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
|
|
@@ -24,7 +25,7 @@ function replaceArrayBufferWithPlaceholder<In extends any>(
|
|
|
24
25
|
if ((obj as any) === arrayBuffer) {
|
|
25
26
|
return arrayBufferPlaceholder as any
|
|
26
27
|
} else if (Array.isArray(obj)) {
|
|
27
|
-
return (obj as any[]).map(
|
|
28
|
+
return (obj as any[]).map(element => replaceArrayBufferWithPlaceholder(element, arrayBuffer)) as any
|
|
28
29
|
} else if (obj && typeof obj === 'object') {
|
|
29
30
|
const result: In = Object.create(Object.getPrototypeOf(obj))
|
|
30
31
|
|
|
@@ -43,7 +44,7 @@ test('can pass transferable objects on thread call', async (t) => {
|
|
|
43
44
|
const worker = new Worker('./workers/arraybuffer-xor')
|
|
44
45
|
const postMessageCalls: Array<any[]> = []
|
|
45
46
|
|
|
46
|
-
worker.postMessage = spyOn(worker.postMessage.bind(worker),
|
|
47
|
+
worker.postMessage = spyOn(worker.postMessage.bind(worker), postMessage => (...args) => {
|
|
47
48
|
postMessageCalls.push(replaceArrayBufferWithPlaceholder(args, testData))
|
|
48
49
|
return postMessage(...args)
|
|
49
50
|
})
|
package/test/workers/minmax.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable sonarjs/no-use-of-empty-return-value */
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
2
|
|
|
4
3
|
import { isWorkerRuntime, Pool, spawn, Worker } from '../../src/index'
|
|
@@ -9,10 +8,10 @@ type HelloWorker = (text: string) => string
|
|
|
9
8
|
async function test() {
|
|
10
9
|
const pool = Pool(() => spawn<HelloWorker>(new Worker('./pool-worker')))
|
|
11
10
|
const results = await Promise.all([
|
|
12
|
-
pool.queue(
|
|
13
|
-
pool.queue(
|
|
14
|
-
pool.queue(
|
|
15
|
-
pool.queue(
|
|
11
|
+
pool.queue(hello => hello('World')),
|
|
12
|
+
pool.queue(hello => hello('World')),
|
|
13
|
+
pool.queue(hello => hello('World')),
|
|
14
|
+
pool.queue(hello => hello('World')),
|
|
16
15
|
])
|
|
17
16
|
await pool.terminate()
|
|
18
17
|
|