Open-source project
parallel-js/parallel.js avatar
parallel-js/parallel.js

Parallel.js runs one wrapped array across workers and child processes

GitHub describes it as Easy multi-core processing utilities for Node.. The repository metadata lists JavaScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

3,244 stars197 forksJavaScriptMIT

At a glance

What is it?
paralleljs takes an array and applies map, reduce or spawn over it using Web Workers in the page and child processes in Node. The API is small, but serializability, evalPath and maxWorkers decide what actually runs.
Who is it for?
Adopt paralleljs when you have an array large enough to pay for process setup and functions pure enough to survive copying into a worker. Skip it for small payloads, for work that needs shared mutable state between threads, or for Node versions below the v14.18.1 floor the package declares.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 20 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • parallel-js/parallel.js3,244 ★ · -1
Star history
Repository2026-08-132026-10-04
parallel-js/parallel.js3.2k3.2k

-1 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The browser build blanks out child_process

package.json points main at lib/parallel.js and gives that one file two lives. The browser field maps child_process to false, so a bundler substitutes an empty module when the page loads the library and the runtime that reaches for process spawning disappears from browser output. Nothing else in the manifest changes between targets, which means a single entry file carries the whole branch. The stated split is short: Parallel takes advantage of Web Workers for the web, and child processes for Node. The browser matrix in the testling block reaches back to ie/9..latest, chrome/22..latest, firefox/16..latest, safari/latest and android-browser/latest, and the repository keeps a SpecRunner.html at the top level so the specs can run in a browser with no package install at all. The practical consequence for a reader is that the API surface documented for Node is the same one a page receives, and the only difference lives underneath it in how a worker gets created.

maxWorkers defaults to your CPU count under Node

maxWorkers is the option that decides how much of your array runs at once. The constructor takes the maximum number of permitted worker threads, and that value will default to 4, or the number of cpus on your computer if you're running node. The same constructor argument therefore produces a different fleet size per runtime: a fixed four in a page, one worker per core on a machine running Node. map applies the supplied function to every element in the wrapped data, and Parallel will spawn one worker for each array element in the data, or the supplied maxWorkers argument. Read those two statements together and the behaviour is clear: a two element array still draws on the default fleet rather than exactly two workers, and a very large array is quietly truncated at the cap. Wrapping a small payload in a job object therefore costs more than mapping it inline, and nothing here reports what that oversubscription costs.

Your callback has to survive being copied

The only restriction placed on the data argument is that your values are serializable as JSON. The map example carries an explicit warning comment: anonymous functions cannot be serialzed. That is why the sample declares function fib(n) as a named definition before calling p.map(fib), and why the comment adds that if you want to do recursion, make sure the function is named appropriately. The reduce section reaches the same rule from the other direction, calling p.require(factorial) before p.map(n => Math.pow(10, n)).reduce(add), so the recursive factorial is defined once in the main thread and handed across the boundary rather than inlined at each call site. The two examples describe two routes to one constraint. Anything you pass in must be a value the worker can rebuild, and anything you pass in as a function must be a name the worker can resolve.

evalPath decides whether a worker can boot at all

evalPath is the path to the file eval.js, and it is the option that will block a first run more often than not. It is required when running in node, and required for some browsers (IE 10) in order to work around cross-domain restrictions for web workers. Its default is the same location as parallel.js in node environments, and null in the browser. require leans on the same setting, because using require with a file name as an argument requires evalPath in the options object:

js
let p = new Parallel([1, 2, 3], { evalPath: 'https://raw.github.com/parallel-js/parallel.js/master/lib/eval.js' });

const cubeRoot = n => Math.pow(n, 1 / 3);

// Require a function
p.require(cubeRoot);

// Require a file
p.require('blargh.js');

p.map(d => blargh(20 * cubeRoot(d)));

Note where that evalPath points: a raw content URL fetched at spawn time, so every worker bootstraps by loading code over the network. The README does not document a bundled copy or a local path for eval.js, which makes this a deployment decision rather than a default.

spawn can kill a worker that overruns its timeout

spawn(fn, opts) spawns a new process on a worker thread and hands your function the current data, whose return value is assigned back to that data. The one option under spawn is opts.timeout, described as milliseconds to way for function to return value; if the worker does not finish in this time, it will be killed. So a slow callback is not merely slow, it is destroyed, and the failure then has to surface somewhere. The only handling described anywhere is the pair handed to then, where fail will receive an error object. No retry, no partial result, and no identifier for the element that was in flight when the worker died appear in the documentation. The separate constructor argument synchronous is not about this: it governs whether to fall back to synchronous processing using setTimeout when webworkers are not available, and defaults to true. A browser without workers therefore degrades to serial timer driven execution rather than raising an error.

Environment data crosses the boundary read only

Values that every worker should see go in through the env constructor option rather than through the data array. That data will be global to the worker and global in each called function, available under the global.env namespace, and the namespace itself can be renamed with the envNamespace option. The constraint is stated in one line and it is absolute: globals can not be mutated between threads. So a worker can read configuration handed down at construction, but a value one worker computes and another expects to see will not arrive, and the mechanism offers no sync primitive, no shared buffer and no callback channel back to the main thread.

js
let p = new Parallel([1, 2, 3], {
  env: {
    a: 10
  }
});

// returns 10, 20, 30
p.map(d => d * global.env.a);

// Configure the namespace
p = new Parallel([1, 2, 3], {
  env: {
    a: 10
  },
  envNamespace: 'parallel'
});

p.map(d => d * global.parallel.a);

Rename the namespace and every callback body has to agree on the new prefix, since the worker has no way to discover what the main thread called it.

The two install paths name different releases

The package name in package.json is paralleljs, not parallel.js, so the registry install is:

bash
npm install paralleljs

The browser fallback quoted in the README pins an older line, serving [email protected] from unpkg while the manifest itself reads version 1.1.0. Anyone who reaches for the script tag on a page is therefore a version behind anyone who imports the module, and the gap is invisible until behaviour differs between environments. The declared floor is engines node >= v14.18.1. Release history is thin: v1.1.0 shipped on 2020-10-06, preceded by v0.2 (New Syntax) on 2013-06-14, while master itself was last pushed on 2026-10-02. Tooling around the package is minimal. The build script is a placeholder that echoes a message about not needing to build, lint runs eslint lib, and test runs jasmine-node --verbose test/specs. yarn.lock is committed alongside appveyor.yml and .travis.yml, and lib/ holds a single entry file.

then gives you a result or an error, and nothing else

then(success, fail) is the only completion signal in the library. The functions given to then are called after the last requested operation has finished, with success receiving the resulting data object and fail receiving an error object. Ordering comes from the chain itself rather than from any scheduler, because map stores the values returned for further processing and the example runs three map passes in sequence to turn [1, 2, 3] into [8, 16, 24]. reduce is the odd one out, applying an operation to every member of the wrapped data and returning a scalar value produced by the operation, taking a reducing function that gets data, an array of the stored value, and the current element. The README does not document how to cancel a chain that is already running, and there is no documented way to read partial results while workers are still busy. That leaves fail as the only exit, with no recovery path in between.

Editorial conclusion

Adopt paralleljs when you have an array large enough to pay for process setup and functions pure enough to survive copying into a worker. Skip it for small payloads, for work that needs shared mutable state between threads, or for Node versions below the v14.18.1 floor the package declares. Before committing, check that your callbacks are named, that evalPath resolves in your environment, and that the child_process stub in the browser field does not surprise your bundler.

Frequently asked questions

What does parallel mean in coding?

In paralleljs it means running your callback over a wrapped array on something other than the main thread: Web Workers for the web, and child processes for Node. The data stays held in memory until you finish your job and stays reachable through the .data attribute.

Is parallelism the same as multithreading?

The option is called maxWorkers and it caps the maximum number of permitted worker threads, defaulting to 4 or the number of cpus on your computer if you're running node. map spawns one worker for each array element in the data, or the number you supply.

Is parallel programming difficult?

The chain is short: spawn, map, reduce, require and then. The friction is in the boundary conditions. Your values must be serializable as JSON, anonymous functions cannot be serialized, and calling require with a file name requires the evalPath option to be set.

What is a parallel programming language?

paralleljs is not a language. The repository is JavaScript and the package is a library that works in Node.js and in the web browser, with main set to lib/parallel.js and the browser build mapping child_process to false.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/parallel-js-parallel-js.svg)](https://hysenlabs.com/projects/parallel-js-parallel-js)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/parallel-js-parallel-js.svg)](https://hysenlabs.com/projects/parallel-js-parallel-js)