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: