How to write a Promise wrapper around Web Workers API?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Web Workers are already asynchronous, but the raw event-based API is awkward when you want a clean request-result flow. A Promise wrapper lets you treat a one-off worker job like any other async function: send input, await output, and handle errors in one place.
The Core Worker Contract
A worker communicates with the main thread through messages. The main thread posts data with postMessage, and the worker replies with its own postMessage.
A minimal worker file might look like this:
Without a wrapper, the caller has to manage onmessage, onerror, and cleanup manually every time.
A Simple Promise Wrapper
For one-shot jobs, wrap the worker in a Promise and terminate it when the result arrives.
This gives you a normal Promise-based interface while still using the worker thread underneath.
Why This Pattern Works Well
A Promise wrapper is a good fit when each worker instance does exactly one job. It simplifies these concerns:
- success handling
- failure handling
- cleanup with
terminate() - '
awaitintegration in calling code'
The caller no longer needs to remember worker lifecycle details for every use.
Add Structured Error Handling
Worker runtime errors and application-level errors are different. If your worker code can fail in a normal way, send a structured message instead of relying only on onerror.
Then unwrap it in the Promise wrapper:
That separates JavaScript execution errors from your own domain-level validation failures.
When One Worker Per Call Is Not Enough
If you need to reuse a worker for many requests, a single Promise wrapper is not enough by itself. You then need request IDs so multiple outstanding messages can be matched to the correct Promise.
That is a more advanced pattern, but the same idea holds: store a resolver and rejector for each request, and clear them when the matching response arrives.
Common Pitfalls
A common mistake is forgetting to terminate a one-shot worker. That leaves background threads alive longer than necessary.
Another mistake is assuming onerror catches every logical failure. It only catches worker script errors, not all application-level bad input. Send structured error messages when the worker can reject validly.
A third issue is posting data that cannot be cloned efficiently. Large payloads can make worker communication slower than expected, so consider transferables when moving big buffers.
Summary
- A Promise wrapper makes one-off worker usage much cleaner
- Wrap
onmessageandonerrorinside a Promise and terminate after completion - Use structured messages if the worker needs to report application-level failures
- Reused workers require request IDs, not just one Promise for the whole worker
- Watch payload size because worker messaging still has transfer costs
Related reading
- How to write a scalable TCP/IP based server
- How would I set up access to multiple Nodes with a single Service in kubernetes?
- HTTP 404 when accessing .svc file in IIS
- Http 415 Unsupported Media type error with JSON
- How to write Asynchronous LINQ query?
- How to write Kafka consumers - single threaded vs multi threaded
- How to yum install Node.js on Amazon Linux
- How would I get webpack or an other JS bundler to bundle files remotely hosted?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.