async-worker-ts
v1.0.14
Published
🔱 A type-safe package designed to simplify the usage of worker threads on the server or browser.
Downloads
18
Maintainers
Readme
async-worker-ts 🔱
A type-safe package designed to simplify the usage of worker threads on the server or browser.
Usage:
import createWorker, { task } from "async-worker-ts"
const worker = createWorker({
calculatePi: (iterations: number) => {
let pi = 0
for (let i = 0; i < iterations; i++) {
pi += Math.pow(-1, i) / (2 * i + 1)
}
return pi * 4
},
todos: {
get: () => {
// ...
},
add: () => {
// ...
},
delete: () => {
// ...
},
},
})
await worker.calculatePi(1_000_000).then(console.log) // 3.14159265258979
await worker.exit() // terminates the worker thread
Accessing procedures within procedures:
import useWorker from "async-worker-ts"
const worker = useWorker({
/**
* NB; the 'this' keyword is available in procedures declared as anything
* but arrow functions and can be used to access other procedures.
*/
addRandomNumbers: function () {
const a = this.randomNumber()
const b = this.randomNumber()
return a + b
},
randomNumber: () => {
return Math.random() * 42
},
})
Emitting data via Tasks:
import useWorker, { task } from "async-worker-ts"
const worker = useWorker({
calculatePi: task(function (iterations: number) {
let pi = 0
for (let i = 0; i < iterations; i++) {
pi += Math.pow(-1, i) / (2 * i + 1)
// the "this" keyword in the context of a task refers to the task itself.
if (i % (iterations / 100) === 0) this.emit("progress", i / iterations)
}
return pi * 4
}),
})
await worker
.calculatePi(1_000_000)
.on("progress", console.log) // 0.01, 0.02, ...
.then(console.log) // 3.14159265258979
Concurrency and batching:
import useWorker from "async-worker-ts"
const worker = useWorker({
calculatePi: (iterations: number) => {
let pi = 0
for (let i = 0; i < iterations; i++) {
pi += Math.pow(-1, i) / (2 * i + 1)
}
return pi * 4
},
})
/**
* We can use the 'concurrently' method to run a task from the worker
* client in a new auto-disposed worker thread.
*/
worker.concurrently((w) => w.calculatePi(1_000_000)) // 3.14159265258979
/** or: */
for (let i = 0; i < 4; i++) {
worker.concurrently((w) => w.calculatePi(1_000_000))
}
Transferables:
import useWorker, { transfer } from "async-worker-ts"
const worker = useWorker({
drawToCanvas: (OffscreenCanvas) => {
// ... do things with the canvas here as if we were on the main thread
},
})
const canvas = document.createElement("canvas")
const offscreenCvs = canvas.transferControlToOffscreen()
/**
* By passing the argument through the 'transfer' function, we flag it as an
* transferable. This is the equivalent of calling 'postMessage' with the
* second argument being an array containing the argument.
*/
worker.drawToCanvas(transfer(offscreenCvs))
Dynamic imports and module resolution:
Importing modules requires a bundler for module resolution because procedures are serialized and executed in a different scope, rendering relative paths useless. I created awt-bundler as a simple bundler for using this package with Node.
someModule.ts:
export const someFunction = () => {
// ...
}
myWorker.ts:
import useWorker, { AWTClientBuilder } from "async-worker-ts"
const worker = useWorker({
doSomething: async () => {
const { someFunction } = await import("./someModule.ts")
return someFunction()
},
})
// or:
const workerWithCachedImports = new AWTClientBuilder()
.withImportCache(async () => {
const { someFunction } = await import("./someModule.js")
return { someFunction }
})
.build(function ({ someFunction }) {
return {
doSomething: () => {
return someFunction()
},
}
})