Show HN: Easily compute expensive functions inside type safe Web Workers
2 points by jaaamesey 4 months ago | 0 commentsIn short, this JS library allows you to quickly compute functions in a separate thread like so:
```
const result = runWithWorker(() => 1 + 1); // TypeScript knows this is a `Promise<number>`!
```
There's some more examples outlined in the README. Communicating with Workers involves a few ms of overhead, so this is only going to be useful for computations that would take longer than that.
Throwing two or more of these calls into `Promise.all` should also let you make use of multiple threads for a computation.
Some background: the puzzle creator (coming soon!) for https://bridj.link inherently takes up to a few seconds to generate a "good" set of tiles. More importantly, there's no fast way to determine if there's no viable set of tiles to connect two words - so we have to reject the puzzle if it takes longer than 10 seconds for the algorithm to solve.
This seemed like a textbook use case for moving computation to a Web Worker - it wouldn't freeze the UI thread (meaning users didn't have a completely frozen tab while waiting), and we could easily terminate it if computation hanged or the user chose to cancel.
If all you want to do with a Web Worker is execute a single function though, there's a surprising amount of complexity involved. Beyond the boilerplate, there's a lot that can go wrong regarding proper error handling and remembering to actually terminate a worker: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers...
This becomes even more complex if you want it to play nice with TypeScript. Also, most projects these days still use some kind of bundler, which might require some extra steps for loading a script into a worker.
This inspired me to make a library that takes some of that complexity away, for use cases that just involve running a single function in a worker.
There's some limitations outlined in the README, but it:
- works in any JS environment that supports Workers
- lets you pass imported modules to the Worker, even in Vite/Webpack
- lets you pass down parameters to the Worker (provided they're compatible with `structuredClone`)
- is Promise-based, and allows the Worker function to be async
- lets you terminate the Worker manually or after a certain length of time
That part about passing down parameters and imports is important, as there's no other way for functions to capture their outside scope. Workers are still an isolated environment, so any context from the outside needs to be passed down explicitly:
```
const x = 2; runWithWorker(x => x + 1, [x]);
```
I couldn't find a library with the same ergonomics that met those criteria, which is why I decided to write my own. Lemme know what y'all think!