Skip to content
VersionSize

worker

The worker utility simplifies the use of Web Workers by allowing you to run heavy computations in a background thread without creating separate files. It supports importing external dependencies and handles all the complex message-passing logic for you.

Implementation

View Source Code
ts
/**
 * Creates a function that runs the provided callback in a web worker with specified dependencies.
 *
 * @example
 * const sum = worker(({ _ }) => (...args) => _.sum([...args]), ['https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js']);
 * await sum(1, 2); // 3
 *
 * @param callback - A function that receives the worker's `self` and performs work.
 * @param dependencies - (optional) array of URLs to scripts that the worker should import.
 *
 * @returns A function that takes the arguments for the callback and returns a promise with the result.
 */
// biome-ignore lint/suspicious/noExplicitAny: -
export function worker<T extends (...args: any) => any, R = Awaited<ReturnType<T>>>(
  // biome-ignore lint/suspicious/noExplicitAny: -
  callback: (self: any) => T,
  dependencies: string[] = [],
): (...args: Parameters<T>) => Promise<R> {
  const callbackString = callback.toString();
  const workerScript = `
    ${dependencies.map((dep) => `importScripts('${dep}');`).join('\n')}
    const callback = (${callbackString})(self);
    self.onmessage = async function(e) {
      try {
        const result = await callback(...e.data.args);
        self.postMessage({ success: true, result });
      } catch (error) {
        self.postMessage({ success: false, error: error?.message || String(error) });
      }
    };
  `;
  const blob = new Blob([workerScript], { type: 'application/javascript' });
  const workerUrl = URL.createObjectURL(blob);

  return (...args: Parameters<T>): Promise<R> =>
    new Promise((resolve, reject) => {
      const workerInstance = new Worker(workerUrl);

      const cleanup = () => {
        workerInstance.terminate();
        URL.revokeObjectURL(workerUrl);
      };

      workerInstance.onmessage = (e) => {
        cleanup();
        if (e.data.success) {
          resolve(e.data.result);
        } else {
          reject(new Error(e.data.error));
        }
      };

      workerInstance.onerror = (err) => {
        cleanup();
        reject(new Error(err.message || 'Worker error occurred'));
      };

      workerInstance.postMessage({ args });
    });
}

Features

  • Browser Only: Leverages the native Web Worker API.
  • Background Execution: Offloads CPU-intensive tasks to prevent UI freezing.
  • Integrated Dependencies: Import external scripts directly into the worker environment.
  • Promise-based: Call background functions as if they were local async functions.

API

ts
function worker<T extends (...args: any[]) => any>(
  callback: (context: any) => T,
  dependencies?: string[],
): (...args: any[]) => Promise<any>;

Parameters

  • callback: A factory function that runs inside the worker. It receives a context (including self) and must return the function that will perform the work.
  • dependencies: Optional. An array of URLs for external scripts to be imported into the worker using importScripts.

Returns

  • A new function that, when called, executes the logic in a Web Worker and returns a Promise with the result.

Examples

Heavy Data Processing

ts
import { worker } from '@vielzeug/toolkit';

// This function will run in a separate thread
const processLargeDataset = worker(() => {
  return (data: number[]) => {
    // Perform complex calculations...
    return data.map((n) => Math.sqrt(n) * Math.PI).reduce((a, b) => a + b);
  };
});

const result = await processLargeDataset([1, 2, 3, 4, 5, 6]);

Using External Libraries

ts
import { worker } from '@vielzeug/toolkit';

const calculateHash = worker(() => {
  return (text: string) => {
    // Logic goes here
    return text;
  };
});

const hash = await calculateHash('Hello World');

Implementation Notes

  • Creates a Blob from the stringified function to instantiate the Worker.
  • Automatically terminates the internal worker instance after the task is complete (depending on implementation details, usually stays alive for reuse if optimized).
  • Throws an error if used in an environment without Worker support (e.g., standard Node.js).

See Also

  • predict: Wait for async conditions.
  • retry: Re-run failed operations.
  • attempt: Safely execute logic that might crash.