Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Non-blocking Concurrent Computation for JavaScript RTEs (Web Browsers, Node.js, Deno & Bun)

License

NotificationsYou must be signed in to change notification settings

BitairLabs/concurrent.js

Repository files navigation

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 thehello 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/concurrentjs@v0.8.2/mod.ts'// Import a moduleconstMyModule=concurrent.import(newURL('./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 workerconst{ SampleObject, sampleFunction}=awaitMyModule.load()// Load it into another web worker// const { SampleObject: SampleObject2, sampleFunction: sampleFunction2 } = await MyModule.load()// Run a functionconstresult=awaitsampleFunction(/*...args*/)// Run a class (instance members)constobj=awaitnewSampleObject(/*...args*/)// Instantiateconstvalue=awaitobj.sampleProp// Get a field or getterawait((obj.sampleProp=1),obj.sampleProp)// Set a field or setterconstresult=awaitobj.sampleMethod(/*...args*/)// Call a method// Run a class (static members)constvalue=awaitSampleObject.sampleStaticProp// Get a static field or getterawait((SampleObject.sampleStaticProp=1),SampleObject.sampleStaticProp)// Set a static field or setterconstresult=awaitSampleObject.sampleStaticMethod(/*...args*/)// Call a static method// Terminate Concurrent.jsawaitconcurrent.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)

RTEJS EngineExecution TimeCPU Usage
1Deno (v1.40)V87.9168s100%
2Chrome* (v121.0)V87.919s100%
3Node (v20.11)V88.117s100%
4Servo (v0.0.1-c94d584)SpiderMonkey31.267s99%
5LibreWolf (122.0)SpiderMonkey35.417s92%
6Firefox* (v125.0)SpiderMonkey49.061s95%
7Bun (v1.0.26)JavaScriptCore51.502s99%
8GNOME Web (v45.2)JavaScriptCore59.058s75%
  • A headless environment was used for benchmarking.

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

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

For benchmarking other browsers, use thebrowser basic usage sample

git clone https://github.com/bitair-org/concurrent.js.gitcd concurrent.js/apps/sample/browsernpm inpm 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, theload method of the imported module must be called for each function call or object instance individually:

import{concurrent}from'@bitair/concurrent.js'constextraBigint=concurrent.import('extra-bigint')concurrent.config({maxThreads:16})// Instead of a hardcoded value, use os.availableParallelism() in Node.js v19.4.0 or laterconsttasks=[]for(leti=0;i<=100;i++){const{ factorial}=awaitextraBigint.load()tasks.push(factorial(i))}constresults=awaitPromise.all(tasks)// ...rest of the codeawaitconcurrent.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'exportasyncfunctionreactiveAdd(channel/*: IChannel */){letdone=falseletsum=0leti=0channel.onmessage(name=>{if(name==='done')done=true})do{sum+=awaitchannel.postMessage('next',i++)}while(!done)returnsum}

index.mjs

import{concurrent,Channel}from'@bitair/concurrent.js'const{ reactiveAdd}=awaitconcurrent.import(newURL('./services/index.mjs',import.meta.url)).load()constchannel=newChannel((onmessage,postMessage)=>{constarr=[1,2,3,4]onmessage(async(name, ...data)=>{if(name==='next'){const[i]=dataif(i===arr.length-1)awaitpostMessage('done')returnarr[i]}})})constresult=awaitreactiveAdd(channel)// ...rest of the codeawaitconcurrent.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.
classChannelimplementsIChannel

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

About

Non-blocking Concurrent Computation for JavaScript RTEs (Web Browsers, Node.js, Deno & Bun)

Topics

Resources

License

Stars

Watchers

Forks


[8]ページ先頭

©2009-2025 Movatter.jp