statulr / async-worker-ts

πŸ”± A type-safe package designed to simplify the usage of worker threads on the server or browser.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

async-worker-ts

async-worker-ts is a lightweight TypeScript package designed to simplify the execution of asynchronous workers in TypeScript applications. It provides a simple and efficient way to manage asynchronous tasks in the background, allowing you to offload time-consuming operations without blocking the main thread.

Features

  • Asynchronous Workers: Easily create and run asynchronous workers in TypeScript.
  • True multithreading: Execute procedures in multiple threads simultaneously to improve performance.
  • Promise-based API: Simple and intuitive API based on Promises for easy integration.

Installation

Install the package using npm:

npm install async-worker-ts

or

pnpm add async-worker-ts
import createWorker, { task } from "async-worker-ts"

const worker = createWorker({
  calculatePi: (iterations: number) => {
    let pi = 0
    for (let i = 0; i < iterations; i++) {
      pi += Math.pow(-1, i) / (2 * i + 1)
    }
    return pi * 4
  },

  todos: {
    get: () => {
      // ...
    },
    add: () => {
      // ...
    },
    delete: () => {
      // ...
    },
  },
})

await worker.calculatePi(1_000_000).then(console.log) // 3.14159265258979
await worker.exit() // terminates the worker thread

Accessing procedures within procedures:

import useWorker from "async-worker-ts"

const worker = useWorker({
  /**
   * NB; the 'this' keyword is available in procedures declared as anything
   * but arrow functions and can be used to access other procedures.
   */
  addRandomNumbers: function () {
    const a = this.randomNumber()
    const b = this.randomNumber()
    return a + b
  },
  randomNumber: () => {
    return Math.random() * 42
  },
})

Emitting data via Tasks:

import useWorker, { task } from "async-worker-ts"

const worker = useWorker({
  calculatePi: task(function (iterations: number) {
    let pi = 0
    for (let i = 0; i < iterations; i++) {
      pi += Math.pow(-1, i) / (2 * i + 1)

      // the "this" keyword in the context of a task refers to the task itself.
      if (i % (iterations / 100) === 0) this.emit("progress", i / iterations)
    }
    return pi * 4
  }),
})

await worker
  .calculatePi(1_000_000)
  .on("progress", console.log) // 0.01, 0.02, ...
  .then(console.log) // 3.14159265258979

Concurrency and batching:

import useWorker from "async-worker-ts"

const worker = useWorker({
  calculatePi: (iterations: number) => {
    let pi = 0
    for (let i = 0; i < iterations; i++) {
      pi += Math.pow(-1, i) / (2 * i + 1)
    }
    return pi * 4
  },
})

/**
 * We can use the 'concurrently' method to run a task from the worker
 * client in a new auto-disposed worker thread.
 */
worker.concurrently((w) => w.calculatePi(1_000_000)) // 3.14159265258979

/** or: */
for (let i = 0; i < 4; i++) {
  worker.concurrently((w) => w.calculatePi(1_000_000))
}

Transferables:

import useWorker, { transfer } from "async-worker-ts"

const worker = useWorker({
  drawToCanvas: (OffscreenCanvas) => {
    // ... do things with the canvas here as if we were on the main thread
  },
})
const canvas = document.createElement("canvas")
const offscreenCvs = canvas.transferControlToOffscreen()

/**
 * By passing the argument through the 'transfer' function, we flag it as an
 * transferable. This is the equivalent of calling 'postMessage' with the
 * second argument being an array containing the argument.
 */
worker.drawToCanvas(transfer(offscreenCvs))

Dynamic imports and module resolution:

Importing modules requires a bundler for module resolution because procedures are serialized and executed in a different scope, rendering relative paths useless. I created awt-bundler as a simple bundler for using this package with Node.

someModule.ts:

export const someFunction = () => {
  // ...
}

myWorker.ts:

import useWorker, { AWTClientBuilder } from "async-worker-ts"

const worker = useWorker({
  doSomething: async () => {
    const { someFunction } = await import("./someModule.ts")
    return someFunction()
  },
})

// or:

const workerWithCachedImports = new AWTClientBuilder()
  .withImportCache(async () => {
    const { someFunction } = await import("./someModule.js")
    return { someFunction }
  })
  .build(function ({ someFunction }) {
    return {
      doSomething: () => {
        return someFunction()
      },
    }
  })

God help your CPU. πŸ™

Richard Ayoade using async-worker-ts

Contributing

We welcome contributions from the community. To contribute to async-worker-ts, please follow our contribution guidelines.

License

This package is licensed under the GNUV3 License - see the LICENSE file for details.

About

πŸ”± A type-safe package designed to simplify the usage of worker threads on the server or browser.

License:GNU General Public License v3.0


Languages

Language:TypeScript 83.2%Language:JavaScript 11.3%Language:CSS 4.3%Language:HTML 0.9%Language:Makefile 0.2%