DEV Community

loading...

Making web workers more accessible with closures and Promises

EnderShadow8
・1 min read

If you're like me, you'll find the web workers API quite hard to use at times, because of all the event handlers.

Here's some sample code:

// main.js
const worker = new Worker(workerfile)
worker.onmessage = function(e) {
  doStuff(e.data)
}
Enter fullscreen mode Exit fullscreen mode

It's hard to coordinate other tasks with worker messages only using this API. For example, how would you return the result of a worker calculation from a function? At first, there's no obvious easy way to do so.

Thankfully, there's a clean way around this, which wraps message handlers in a Promise.

// main.js

const worker = new Worker(workerfile)
let outsideResolve, outsideReject

const promise = new Promise((resolve, reject) => {
  outsideResolve = resolve
  outsideReject = reject
}
worker.onmessage = function(e) {
  outsideResolve(e.data)
}
Enter fullscreen mode Exit fullscreen mode

This idea uses closures to pass the resolve function from inside the Promise callback into the parent scope, and then into the web worker event handler. Now the worker can resolve the Promise itself without needing access to the inner scope of the Promise callback.

You can wrap the entire thing inside a function and return the Promise. Now, all you need to do to communicate with a web worker is call an async function!

Here's a small (contrived) example:

// main.js

const worker = new Worker("worker.js")
let outsideResolve
worker.onmessage = function(e) {
  outsideResolve(e.data)
}

function calculateSomething(n) {
  return new Promise((resolve, reject) => {
    worker.postMessage(n)
    outsideResolve = resolve
  })
}

const result = await calculateSomething(123)
Enter fullscreen mode Exit fullscreen mode

Hope this helps!

Discussion (0)