Skip to content
Advertisement

Canonical way to run an array of functions in Javascript/Node

I have found two handy modules called run-parallel and run-series to run arrays functions and return arrays of results.

Looking at the little number of contributors and stars in the Github projects, I wonder if there is a canonical way of doing these tasks instead of installing these modules?

Maybe there is a native way in Node or in ES6 to do this that I am missing?

Advertisement

Answer

Examples

First see some examples – scroll below for an explanation.

Callbacks:

Example with async and functions that take Node-style callbacks:

async.parallel([
  (cb) => {
    setTimeout(() => {
      cb(null, 'one');
    }, 200);
  },
  (cb) => {
    setTimeout(() => {
      cb(null, 'two');
    }, 100);
  },
],
(err, results) => {
  if (err) {
    // there was an error:
    console.log('Error:', err);
    return;
  }
  // we have ['one', 'two'] in results:
  console.log('Results:', JSON.stringify(results));
});

Promises:

Example using functions that return promises – with Bluebird’s delay() function:

const { delay } = require('bluebird');

Promise.all([
  delay(200, 'one'),
  delay(100, 'two'),
]).then((results) => {
  // we have ['one', 'two'] in results:
  console.log('Results:', JSON.stringify(results));
}).catch((err) => {
  // there was an error:
  console.log('Error:', err);
});

ES2017 async/await:

Using async/await:

const { delay } = require('bluebird');

try {
  const results = await Promise.all([
    delay(200, 'one'),
    delay(100, 'two'),
  ]);
  // we have ['one', 'two'] in results:
  console.log('Results:', JSON.stringify(results));
} catch (err) {
  // there was an error:
  console.log('Error:', err);
}

I’m using JSON.stringify() to make it explicit what is the format of the data in results.

Note that even though the first value comes last, the original order is still preserved.

The last example must be run inside of a function declared with an async keyword, or wrapped in (async () => { ... })() like this:

(async () => {
  try {
    const results = await Promise.all([
      delay(200, 'one'),
      delay(100, 'two'),
    ]);
    // we have ['one', 'two'] in results:
    console.log('Results:', JSON.stringify(results));
  } catch (err) {
    // there was an error:
    console.log('Error:', err);
  }
})();

Generators and coroutines:

With no support for async/await you can use some generator-based coroutines, like those that comes from Bluebird:

const { delay, coroutine } = require('bluebird');

coroutine(function* () {
  try {
    const results = yield Promise.all([
      delay(200, 'one'),
      delay(100, 'two'),
    ]);
    // we have ['one', 'two'] in results:
    console.log('Results:', JSON.stringify(results));
  } catch (err) {
    // there was an error:
    console.log('Error:', err);
  }
})();

Explanation

There are many ways to do that and it all depends on the kind of functions that you want to run.

If you want to run a traditional Node-style functions that take error-first callbacks as their last arguments then the most popular way to run those in parallel or in series is the async module on npm:

There is no built-in support for things like that in ES6 because those error-first callbacks are really a Node thing, not very popular in JavaScript outside of Node.

The ES6/ES7/ES8 goes in the direction of functions that return promises (instead of functions that take callbacks) and there is a new async/await syntax to make them look kind-of-like synchronous, with try/catch working for error handling.

So, the most popular way in Node to combine functions taking callbacks is async module:

To work with promises a popular modules is Bluebird:

For more advanced tasks there is Task.js:

See those answers for more info:

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement