@arrows/worker
Version:
Simple, promise-based API for native worker threads (with pools)
72 lines (58 loc) • 1.87 kB
text/typescript
import { Worker, MessagePort } from "worker_threads"
import { cpus } from "os"
import { Spawn, Task } from "./types"
const defaultConfig = {
poolSize: cpus().length,
}
/**
* Spawns a workers pool from worker defined in a separate file,
* returns a function that handles messaging and returns responses as promises.
*
* Use when you want to separate worker definition from spawning a thread pool.
*
* @param fileName Path to a worker definition file created with `work` function
* @param config Configuration options
* @returns Async function that communicates with worker threads
*/
const spawn: Spawn = (fileName, config = {}) => {
const cfg = { ...defaultConfig, ...config }
const { poolSize, transfer, ...options } = cfg
if (poolSize <= 0) {
throw new Error("Pool size has to be > 0")
}
const promises = new Map()
const workers = Array.from({ length: poolSize }, () =>
new Worker(fileName, options).on("message", ([id, data, error]) => {
if (error) {
promises.get(id).reject(error)
} else {
promises.get(id).resolve(data)
}
promises.delete(id)
}),
)
let workerIndex = 0
const fn = (payload: any, transferList?: (ArrayBuffer | MessagePort)[]) => {
const id = process.hrtime.bigint()
const promise = new Promise((resolve, reject) => {
promises.set(id, { resolve, reject })
})
workers[workerIndex].postMessage([id, payload], transferList)
workerIndex = (workerIndex + 1) % poolSize
return promise
}
const task: Task<any, any> = Object.assign(fn, {
ref() {
workers.forEach((worker) => worker.ref())
},
unref() {
workers.forEach((worker) => worker.unref())
},
terminate() {
return Promise.all(workers.map((worker) => worker.terminate()))
},
})
return task
}
export { spawn }
export default spawn