1

Let's say I have this async.parallel code example below, to run async functions in parallel and wait for them to complete before running code on results.

async.parallel({
  one: async (callback) => {/* ... some async code */ return someResult},
  two: async (callback) => {/* ... some async code */ return someResult},
  three: async (callback) => {/* ... some async code */ return someResult},
},(err,results) => {
  if (err) { /* … error handling code */ }
  /* … continued code */
  debug(results)      // prints "results" object successfully without problems
});

What I want to do is to put continued code (code to continue/run after all functions passed to async.parallel are completed) outside async.parallel.

I tried this:

const results = await async.parallel({
  one: async (callback) => {/* ... some async code */ return someResult},
  two: async (callback) => {/* ... some async code */ return someResult},
  three: async (callback) => {/* ... some async code */ return someResult},
},(err,results) => {
  if (err) { /* … error handling code */ }
  debug("test print1: from inside async.parallel callback function");
        // called/printed after "test print2" meaning that async.parallel isn't awaited.
  return results;
});

/* … continued code */
debug("test print2: from outside async.parallel");
    // printed before "test print1" meaning that async.parallel isn't awaited.
debug(results)      // prints "undefined"

And this:

let results_outer;
await async.parallel({
  one: async (callback) => {/* ... some async code */ return someResult},
  two: async (callback) => {/* ... some async code */ return someResult},
  three: async (callback) => {/* ... some async code */ return someResult},
},(err,results) => {
  if (err) { /* … error handling code */ }
  debug("test print1: from inside async.parallel callback function");
        // printed after "test print2" meaning that async.parallel isn't awaited.
  results_outer = results;
});

/* … continued code */
debug("test print2: from outside async.parallel");
    // printed before "test print1" meaning that async.parallel isn't awaited.
debug(results_outer)      // prints "undefined"

My question is, How can I await async.parallel and get results object successfuly?


Suggested Solutions/Alternatives: (Non is yet a solution for my question, however they may be useful temporary alternatives)

Alternative 1: (I don't prefer because it seems uglier/cumbersome than async.parallel)

const results = await Promise.all([
  new Promise( (resolve, reject) => {/* ... some async code */ resolve('some result 1')} ),
  new Promise( (resolve, reject) => {/* ... some async code */ resolve('some result 2')} ),
  new Promise( (resolve, reject) => {/* ... some async code */ resolve('some result 3')} )
]);
debug(results);      // prints "results" object successfully without problems

Note on Alternative 1:
To make Alternative 1 simpler, we can create promisify(some_function):

const results = await Promise.all([
  promisfy(some_functionOne),
  promisfy(some_functionTwo),
  promisfy(some_functionThree)
]);
debug(results);      // prints "results" object successfully without problems

function promisfy(some_function) {
  const promise = new Promise( (resolve, reject) => {
    some_function_return_value = await some_function();
    resolve(some_function_return_value);
  } );

  return promise;
}

To simplify Alternative 1 further:

async_parallel([some_functionOne, some_functionTwo, some_functionThree]);
debug(results);      // prints "results" object successfully without problems

function async_parallel(functions_to_run) {
  let promises = functions_to_run.map(function_to_run => promisfy(function_to_run));
  const results = await Promise.all(promises);
  return results;
}

We may end up building a similar library to async library and reinvent the wheel, specially when we need features like concat, waterfall, etc.

Omar
  • 6,681
  • 5
  • 21
  • 36
  • Look at below, this might help. https://stackoverflow.com/questions/47794063/limit-concurrent-requests-to-api-in-nodejs-along-with-async-await – Nagaraja Malla May 18 '21 at 05:46
  • 4
    Why not just `let results = await Promise.all([Car_model.findOne(...), Car_model.findOne(...). Car_model.findOne(...)]);`? Since your db already supports promises, I don't see why the `async` library is needed for this. – jfriend00 May 18 '21 at 05:46
  • Are these mongoose models? Can you use the promise API instead of async? – Matt May 18 '21 at 05:49
  • @jfriend00 yes Promise.all is straightforward with promises, but I'm not dealing with promises directly, I want to work with async functions directly. I've updated my question to make it clearer. mongoose isn't my main focus, as I use other async code also. – Omar May 18 '21 at 07:52
  • @Omar - Well, if you're going to be dealing with lots of asynchronous code, then you SHOULD be using promises for ALL of it. It is the present and future of the Javascript language, particularly when you start getting the benefits of `await` which ONLY works with promises. So, you should NOT be shying away from promises. You should be running TO promises and embracing them and using `Promise.all()` with them. And FYI, ALL `async` functions return a promise. Everyone. That's how they work. So, all your `async` functions already returned a promise. – jfriend00 May 18 '21 at 08:00
  • 1
    Since the advance of promises and `async/await` in the Javascript language and more and more libraries and built-in functions in nodejs supporting promises, there are fewer and fewer reasons to use a library like `async` or `Bluebird` any more. Yes, there are still a few situations such as managing the number of concurrent requests when doing large numbers of parallel operations, but most of the time, you don't need those libraries any more and it's better to just program in the tools that modern Javascript already gives you. – jfriend00 May 18 '21 at 08:03
  • I thought `Promises.all` is older than `async` library :"D – Omar May 18 '21 at 08:22
  • 1
    No, the original `async` library came before Promises and was designed to manage non-promise, plain callback asynchronous operations. It has since been adapted to incorporate promises, but much of what it originally was designed to do can be done without it using the flow control of regular promises now, particularly when you incorporate `async/await`. – jfriend00 May 18 '21 at 08:28
  • Alternative 1 is never how you would or should do it in the real world. You would promisify your asynchronous operations or use an already promisified verion offered in the module and then just call the promisified version with your `Promise.all` rather than wrapping each individual one with `new Promise(...). So, your comparison is not how one should do it. – jfriend00 May 18 '21 at 09:10
  • @jfriend00 You're right, I need to build my own `promisify(some_function)` and other promise/asynchronous code helper functions that may help me write more neat and less code depending on my use case. While `async` library provides many features like these out of the box without the need to rewrite/recode what async already done/provides (reinvent the wheel). So, I made this comparison. – Omar May 18 '21 at 10:02
  • @Omar - First off, `util.promisify()` already exists - built into nodejs. But, MOST asynchronous operations these days already offer promise versions (such as all your database calls, the whole `fs` library, etc...) so you don't have to promisify very much any more since this is the way of the language, all libraries are moving that direction. Once you get more experience with this, you will find that controlling logic flow and error handling is way simpler with promise control flow, not with async library control flow. – jfriend00 May 18 '21 at 10:33
  • @Omar - And coding with promises is not reinventing the wheel, you're moving the direction the Javascript language is going and learning how to use the most modern and productive tools. I can often retrofit `await` into older asynchronous code and remove many lines of complicated code. Try doing branching asynchronous code with and without `await`. It's literally 10 times simpler with promises and `await`. – jfriend00 May 18 '21 at 10:35
  • @Omar - I think your use of `promisify()` in the code you added to your question is also a bit misunderstanding how promises would be used. You don't retrofit promises in at the highest level you use the function. Instead, you put it at the lowest level so the only function you have to call is one that returns a promise. ALL your logic and control flow would be using promises. You are only showing pseudo-code in your function so it's very hard for us to advise more specifically or show you examples of how to do it with your real code. In the future, we can help better if you show real code. – jfriend00 May 18 '21 at 10:39

2 Answers2

4

Since a modern version of your database already supports promises, you don't need the async library to do this. You can, instead, just do this:

let results = await Promise.all([
     Car_model.findOne(...), 
     Car_model.findOne(...), 
     Car_model.findOne(...)
]);
console.log(results);

This code would, of course, need to be inside an async function so you can use await.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

Just remove the callback function, and async.parallel will return a Promise.

async.parallel returns a promise, if a callback is not passed.

Source

const results = await async.parallel({
  one: async (callback) => {/* ... some async code */ return someResult},
  two: async (callback) => {/* ... some async code */ return someResult},
  three: async (callback) => {/* ... some async code */ return someResult},
});
debug(results)      // prints "results" object successfully without problems

and to handle errors, put code within a try..catch:

try {
  let results = await async.parallel({
    one: async (callback) => {/* ... some async code */ return someResult},
    two: async (callback) => {/* ... some async code */ return someResult},
    three: async (callback) => {/* ... some async code */ return someResult},
  });
  debug(results);
} catch(err) {
  /* … error handling code */
}
Omar
  • 6,681
  • 5
  • 21
  • 36