Code Monkey home page Code Monkey logo

concurrent.js's Introduction

Concurrent.js

Change Log | Star History | Community

Concurrent.js is a library that enables non-blocking computation on JavaScript RTEs by dynamically loading a module into a background thread.

Features

  • Sharing workers
  • Parallel execution
  • Reactive concurrency
  • Isolation

Technical Facts

  • Built upon web workers (a.k.a. worker threads).
  • Simplifies the complexity of worker usage by providing a minimal API.
  • Automatically creates and terminates workers.
  • Automatically cleans up a worker's memory.
  • Has no third-party runtime dependency.
  • Written in TypeScript with the strictest ESNext config.
  • Strictly designed to support strongly-typed programming.
  • Packaged as platform-specific bundles that target ES2020.

Hello World!

Save and run the hello world script to see Concurrent.js in action:

bash hello_world.sh

Installation

npm i @bitair/concurrent.js

Usage

At its highest level of design, Concurrent.js is a dynamic module importer that loads a module into a web worker:

import { concurrent } from '@bitair/concurrent.js'
// In Deno
// import { concurrent } from 'https://deno.land/x/[email protected]/mod.ts'

// Import a module
const MyModule = concurrent.import(new URL('./sample_module.js', import.meta.url))
// In a CommonJS module
// const MyModule = concurrent.import(path.join(__dirname, 'sample_module.js'))

// Load it into a web worker
const { SampleObject, sampleFunction } = await MyModule.load()
// Load it into another web worker 
// const { SampleObject: SampleObject2, sampleFunction: sampleFunction2 } = await MyModule.load()

// Run a function
const result = await sampleFunction(/*...args*/)

// Run a class (instance members)
const obj = await new SampleObject(/*...args*/) // Instantiate
const value = await obj.sampleProp // Get a field or getter
await ((obj.sampleProp = 1), obj.sampleProp) // Set a field or setter
const result = await obj.sampleMethod(/*...args*/) // Call a method

// Run a class (static members)
const value = await SampleObject.sampleStaticProp // Get a static field or getter
await ((SampleObject.sampleStaticProp = 1), SampleObject.sampleStaticProp) // Set a static field or setter
const result = await SampleObject.sampleStaticMethod(/*...args*/) // Call a static method

// Terminate Concurrent.js
await concurrent.terminate()

Samples

Benchmark

The following results demonstrate the average execution time and CPU usage of running 10 concurrent calculations (10 iterations) of the factorial of 50,000 on various JavaScript runtime environments (RTEs). These calculations were performed on a Quad-core AMD APU with a base clock rate of 2.2GHz within a freshly installed isolated Ubuntu VM.

(There are 213,237 digits in the factorial of 50,000)

RTE JS Engine Execution Time CPU Usage
1 Deno (v1.40) V8 7.9168s 100%
2 Chrome* (v121.0) V8 7.919s 100%
3 Node (v20.11) V8 8.117s 100%
4 Servo (v0.0.1-c94d584) SpiderMonkey 31.267s 99%
5 LibreWolf (122.0) SpiderMonkey 35.417s 92%
6 Firefox* (v125.0) SpiderMonkey 49.061s 95%
7 Bun (v1.0.26) JavaScriptCore 51.502s 99%
8 GNOME Web (v45.2) JavaScriptCore 59.058s 75%
  • A headless environment was used for benchmarking.

To benchmark Node, Deno, Bun RTEs as well as Chrome and Firefox browsers use the benchmarking app:

git clone https://github.com/bitair-org/concurrent.js.git
cd concurrent.js/apps/benchmark
npm i
npm start # This command starts a web server required by the headless browsers. Do not open the http://127.0.0.1:8080 address
npm run benchmark 

For benchmarking other browsers, use the browser basic usage sample

git clone https://github.com/bitair-org/concurrent.js.git
cd concurrent.js/apps/sample/browser
npm i
npm start # Open the http://127.0.0.1:8080 address in the target browser

Parallelism

To run each function call or object instance on a separate CPU core, the load method of the imported module must be called for each function call or object instance individually:

import { concurrent } from '@bitair/concurrent.js'

const extraBigint = concurrent.import('extra-bigint')

concurrent.config({ maxThreads: 16 }) // Instead of a hardcoded value, use os.availableParallelism() in Node.js v19.4.0 or later

const tasks = []
for (let i = 0; i <= 100; i++) {
  const { factorial } = await extraBigint.load()
  tasks.push(factorial(i))
}

const results = await Promise.all(tasks)
// ...rest of the code

await concurrent.terminate()

Reactive Concurrency

The reactive concurrency feature provides a bidirectional channel for messaging. A message can be replied to by returning a value:

services/index.mjs

// import type { IChannel } from '@bitair/concurrent.js'

export async function reactiveAdd(channel /*: IChannel */) {
  let done = false
  let sum = 0
  let i = 0

  channel.onmessage(name => {
    if (name === 'done') done = true
  })

  do {
    sum += await channel.postMessage('next', i++)
  } while (!done)

  return sum
}

index.mjs

import { concurrent, Channel } from '@bitair/concurrent.js'

const { reactiveAdd } = await concurrent.import(new URL('./services/index.mjs', import.meta.url)).load()

const channel = new Channel((onmessage, postMessage) => {
  const arr = [1, 2, 3, 4]

  onmessage(async (name, ...data) => {
    if (name === 'next') {
      const [i] = data
      if (i === arr.length - 1) await postMessage('done')
      return arr[i]
    }
  })
})

const result = await reactiveAdd(channel)
// ...rest of the code

await concurrent.terminate()

API

concurrent.import<T>(src: URL | string): IConcurrentModule<T>

Prepares a module to be loaded into workers. Note that only functions and classes can be imported.

  • src: URL | string

    Source of the module. Must be either a URL or a package name. Note that passing a package name is only applicable in Node.js.

IConcurrentModule<T>.load() : Promise<T>

Loads the module into a worker.

concurrent.config(settings: ConcurrencySettings): void

Configures the global settings of Concurrent.js.

  • settings: ConcurrencySettings

    • settings.maxThreads: number [default=1]

      The maximum number of available threads to be spawned.

    • settings.threadIdleTimeout: number | typeof Infinity [default=Infinity]

      Number of minutes before Concurrent.js terminates an idle thread.

    • settings.minThreads: number [default=0]

      The number of threads created when Concurrent.js starts and kept alive to avoid thread recreation overhead.

concurrent.terminate(force?: boolean): Promise<void>

Terminates Concurrent.js.

  • force?: boolean [Not implemented] Forces Concurrent.js to exit immediately without waiting for workers to finish their tasks.
class Channel implements IChannel

Used to send/receive messages to/from functions and methods (instance or static). Note that a function or method can only have one channel argument and it must be the last argument. The channel object cannot be reused to call another function or method.

  • constructor(listener: (onmessage: Channel['onmessage'], postMessage: Channel['postMessage']) => void)
  • onmessage(handler: (name: string | number, ...data: unknown[]) => unknown): void

    Sets the event handler for receiving a message. The handler should return a value if a reply is required for the message.

  • postMessage(name: string | number, ...data: unknown[]): Promise<unknown>

    Sends a message to the other end and returns its reply.

License

MIT License

concurrent.js's People

Contributors

beshan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

concurrent.js's Issues

Usage with tensorflow.js

When trying to use it with tensorflow.js getting an error:

Browser error

Uncaught (in promise) Error: Cannot find module '@tensorflow/tfjs'
    node_modules bundle.js:14
    promise callback*webpackEmptyAsyncContext browser|lazy|groupOptions: {}|strict namespace object:4
    load index.js:264
    tsx Test.tsx:45
    a async module:49
    tsx bundle.js:221
    factory react refresh:6
    Webpack 15
        __webpack_require__
        fn
        ts
        a
        ts
        factory
        __webpack_require__
        fn
        tsx
        a
        tsx
        factory
        __webpack_require__
        <anonymous>
        <anonymous>
browser|lazy|groupOptions: {}|strict namespace object:5

Warning from CRA

WARNING in ./node_modules/@bitair/concurrent.js/src/browser/index.js 297:53-70
Critical dependency: the request of a dependency is an expression

Environment:

    "@bitair/concurrent.js": "^0.6.0",
    "@tensorflow/tfjs": "^4.3.0",
    "@tensorflow/tfjs-vis": "^1.5.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.9.0",
    "react-scripts": "^5.0.1",
    "typescript": "~4.5.4",

Trying to import like this

import * as tftypes from '@tensorflow/tfjs';
const {
  // @ts-ignore
  layers,
} = await concurrent.import('@tensorflow/tfjs').load();
const tfLayers: typeof tftypes.layers = layers;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.