Commit | Line | Data |
---|---|---|
4ade5f1f | 1 | import { AsyncResource } from 'async_hooks' |
fa699c42 | 2 | import { isMainThread, parentPort } from 'worker_threads' |
325f50bc S |
3 | import type { MessageValue } from '../utility-types' |
4 | import type { WorkerOptions } from './worker-options' | |
a32e02ba | 5 | |
a32e02ba | 6 | /** |
4ade5f1f S |
7 | * An example worker that will be always alive, you just need to **extend** this class if you want a static pool. |
8 | * | |
9 | * When this worker is inactive for more than 1 minute, it will send this info to the main thread, | |
10 | * if you are using DynamicThreadPool, the workers created after will be killed, the min num of thread will be guaranteed. | |
11 | * | |
12 | * @author [Alessandro Pio Ardizio](https://github.com/pioardi) | |
a32e02ba | 13 | * @since 0.0.1 |
14 | */ | |
777b7824 | 15 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
4ade5f1f S |
16 | export class ThreadWorker<Data = any, Response = any> extends AsyncResource { |
17 | protected readonly maxInactiveTime: number | |
18 | protected readonly async: boolean | |
19 | protected lastTask: number | |
ee99693b S |
20 | protected readonly interval?: NodeJS.Timeout |
21 | protected parent?: MessagePort | |
4ade5f1f S |
22 | |
23 | public constructor ( | |
24 | fn: (data: Data) => Response, | |
325f50bc | 25 | public readonly opts: WorkerOptions = {} |
4ade5f1f | 26 | ) { |
50811da2 | 27 | super('worker-thread-pool:pioardi') |
4ade5f1f | 28 | |
ee99693b | 29 | this.maxInactiveTime = this.opts.maxInactiveTime ?? 1000 * 60 |
7784f548 | 30 | this.async = !!this.opts.async |
a32e02ba | 31 | this.lastTask = Date.now() |
32 | if (!fn) throw new Error('Fn parameter is mandatory') | |
33 | // keep the worker active | |
34 | if (!isMainThread) { | |
cf9aa6c3 | 35 | this.interval = setInterval( |
fa0f5b28 | 36 | this.checkAlive.bind(this), |
cf9aa6c3 | 37 | this.maxInactiveTime / 2 |
38 | ) | |
fa0f5b28 | 39 | this.checkAlive.bind(this)() |
a32e02ba | 40 | } |
325f50bc S |
41 | parentPort?.on('message', (value: MessageValue<Data>) => { |
42 | if (value?.data && value.id) { | |
43 | // here you will receive messages | |
44 | // console.log('This is the main thread ' + isMainThread) | |
45 | if (this.async) { | |
46 | this.runInAsyncScope(this.runAsync.bind(this), this, fn, value) | |
47 | } else { | |
48 | this.runInAsyncScope(this.run.bind(this), this, fn, value) | |
7784f548 | 49 | } |
325f50bc S |
50 | } else if (value.parent) { |
51 | // save the port to communicate with the main thread | |
52 | // this will be received once | |
53 | this.parent = value.parent | |
54 | } else if (value.kill) { | |
55 | // here is time to kill this thread, just clearing the interval | |
56 | if (this.interval) clearInterval(this.interval) | |
57 | this.emitDestroy() | |
a32e02ba | 58 | } |
325f50bc | 59 | }) |
a32e02ba | 60 | } |
61 | ||
fa0f5b28 | 62 | protected checkAlive (): void { |
cf9aa6c3 | 63 | if (Date.now() - this.lastTask > this.maxInactiveTime) { |
ee99693b | 64 | this.parent?.postMessage({ kill: 1 }) |
a32e02ba | 65 | } |
66 | } | |
106744f7 | 67 | |
fa0f5b28 | 68 | protected run ( |
325f50bc S |
69 | fn: (data?: Data) => Response, |
70 | value: MessageValue<Data> | |
4ade5f1f | 71 | ): void { |
106744f7 | 72 | try { |
8950a941 | 73 | const res = fn(value.data) |
fa0f5b28 | 74 | this.parent?.postMessage({ data: res, id: value.id }) |
106744f7 | 75 | this.lastTask = Date.now() |
76 | } catch (e) { | |
fa0f5b28 | 77 | this.parent?.postMessage({ error: e, id: value.id }) |
106744f7 | 78 | this.lastTask = Date.now() |
79 | } | |
80 | } | |
7784f548 | 81 | |
fa0f5b28 | 82 | protected runAsync ( |
325f50bc S |
83 | fn: (data?: Data) => Promise<Response>, |
84 | value: MessageValue<Data> | |
4ade5f1f | 85 | ): void { |
cf9aa6c3 | 86 | fn(value.data) |
ee99693b | 87 | .then(res => { |
fa0f5b28 | 88 | this.parent?.postMessage({ data: res, id: value.id }) |
cf9aa6c3 | 89 | this.lastTask = Date.now() |
583a27ce | 90 | return null |
cf9aa6c3 | 91 | }) |
ee99693b | 92 | .catch(e => { |
fa0f5b28 | 93 | this.parent?.postMessage({ error: e, id: value.id }) |
cf9aa6c3 | 94 | this.lastTask = Date.now() |
95 | }) | |
7784f548 | 96 | } |
a32e02ba | 97 | } |