Skip to content
easy-web-worker

Getting started

Install easy-web-worker, define the methods of a worker, call them from the main thread, and add progress and cancellation.

GuideTypeScriptTry an example

Install

terminal
npm install easy-web-worker

The package works with any framework, or none, and ships its own TypeScript types. Calls return a CancelablePromise from easy-cancelable-promise, which is installed with it.

Define a worker

A worker is a file that calls defineWorker. The function you pass returns an object, and every key of that object becomes a method that the main thread can call.

math.worker.ts
import { defineWorker } from 'easy-web-worker/defineWorker';

const fibonacci = (n: number): number =>
  n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);

// every key of the returned object is a method of the worker
const worker = defineWorker(() => ({ fibonacci }));

// the main thread only needs this type
export type MathWorker = typeof worker;

The functions are ordinary functions. Nothing about fibonacci knows that it will run on another thread.

Call it from the main thread

createWorker starts the worker and returns an object with the same methods. The main thread imports only the type of the worker, so the code of the worker never ends up in the main bundle.

main.ts
import { createWorker } from 'easy-web-worker/createWorker';
import workerUrl from './math.worker?worker&url';
import type { MathWorker } from './math.worker';

export const math = createWorker<MathWorker>(workerUrl, {
  workerOptions: { type: 'module' },
});

export async function compute() {
  // CancelablePromise<number>: the payload and the result are inferred
  const result = await math.fibonacci(40);

  return result; // 102334155
}

math.fibonacci(40) runs inside a real Web Worker. The page keeps scrolling, typing and animating while it runs, and TypeScript knows the call takes a number and resolves to a number.

Add more methods

A method resolves with the value it returns and rejects with the error it throws. async methods work the same way.

text.worker.ts
import { defineWorker } from 'easy-web-worker/defineWorker';

const worker = defineWorker(() => ({
  countWords: (text: string) => text.trim().split(/\s+/).length,

  // async methods work the same way
  loadReport: async (url: string) => {
    const response = await fetch(url);

    // what a method throws rejects the call on the main thread
    if (!response.ok) throw new Error(`Report not available: ${response.status}`);

    return response.text();
  },
}));

export type TextWorker = typeof worker;

Progress, cancellation and cleanup

Every call returns a CancelablePromise. Progress arrives through onProgress, cancel tells the worker to stop, and unwrap reaches the controls of the worker itself.

lifecycle.ts
import { unwrap } from 'easy-web-worker/createWorker';
import { math } from './main';

export function start(onProgress: (percentage: number) => void) {
  const task = math.fibonacci(42).onProgress(onProgress);

  // stop waiting for it, and tell the worker
  const cancel = () => task.cancel('No longer needed');

  return { task, cancel };
}

// when the feature that owns the worker goes away
export const dispose = () => unwrap(math).dispose();

Reporting progress and reacting to a cancellation happens inside the worker, through the message of the call. Cancellation and progress shows both sides.

Where to go next

  • Typed workers: how methods and types cross the threads.
  • Creating workers: every source createWorker accepts, and the setup for each bundler.
  • Examples: five applications running real Workers, with their source.
Documentation
↑↓ Navigate ↵ Open Esc Closeeasy-web-worker docs
Get started

Real Web Workers. Typed methods.
Cancelable promises.