Web Workers
Workers are the way to run javascript off of the main thread. This can be useful to run compute heavy code without blocking the main thread.
Workers are a statdard, supported by browsers and also server side runtimes like Deno. In node.js, there is the worker_thread package.
An Example
Add a worker.ts:
function findPrime(max) {
for (let i = max; i >= 2; i--) {
let isPrime = true
for (let j = 2; j * j <= i; j ++) {
if (i % j === 0) {
isPrime = false
break
}
}
if (isPrime) return i
}
}
self.onmessage = function(event) {
self.postMessage(findPrime(event.data))
}
Here is how you can invoke it from main thread:
const worker = new Worker(
new URL("./worker.ts", import.meta.url).href,
{type: "module"}
)
worker.postMessage(1_000_000_000_000)
self.onmessage = function(event) {
console.log(event.data)
}
$ deno --allow-read main.ts <<<
999999999989
A Callable Worker
In the previous example, it's hard to get the result of the computation, here is a solution:
class CallableWorker {
private worker: Worker
private id = 0
private calls = new Map()
constructor(url: string) {
this.worker = new Worker(url, {type: "module"})
this.worker.onmessage = (event) => {
const [id, err, result] = event.data
const [resolve, reject] = this.calls.get(id)
err ? reject(err) : resolve(result)
this.calls.delete(id)
}
}
call(method: string, args: any[]) {
this.id ++
const id = this.id
this.worker.postMessage([id, method, args]);
return new Promise((resolve, reject) => {
this.calls.set(id, [resolve, reject])
})
}
}
const worker = new CallableWorker(
new URL("./worker.ts", import.meta.url).href
)
console.log(await worker.call("findPrime", [1_000_000_000_000]))
In worker.ts:
function findPrime(max) {
for (let i = max; i >= 2; i--) {
let isPrime = true
for (let j = 2; j * j <= i; j ++) {
if (i % j === 0) {
isPrime = false
break
}
}
if (isPrime) return i
}
}
const methods = {
findPrime
}
self.onmessage = async function(event) {
const [id, method, args] = event.data
if (!methods[method]) {
self.postMessage([id, new Error(`Method not defined: ${method}`)])
return
}
let result, err
try {
result = await methods[method](...args)
} catch (e) {
err = e
}
self.postMessage([id, err, result])
}