Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching All Promise Rejections in an Async Function in JavaScript

I've ran into an issue with catching all the errors when multiple promises throw rejection errors after being awaited in an async function (javaScript - node v8.4.0).

Make reference to the following javaScript:

For reference, the functions timeoutOne() and timeoutTwo() simply return a native promise that resolves a value after a 1 and 2 second timeout respectively, or reject with an error if I set "deviousState" to true.

let deviousState = true;

async function asyncParallel() {
  try {
    let res1 = timeoutOne();
    let res2 = timeoutTwo();
    console.log(`All done with ${await res1} ${await res2}`)
  }
  catch(err) {
    console.log(err)
  }
}
asyncParallel();

let pAll = Promise.all([timeoutOne(), timeoutTwo()]);
pAll.then((val) => {
  console.log(`All done with ${val[0]} ${val[1]}`)
}).catch(console.log);

In both cases only the promise that returns first logs an error. I know that in some promise libraries there is a way to log all the errors (for example the "settle" method in bluebird), however, I'm not sure if there is an analogue to this method in native promises?

Also, if both promises reject, then asyncParallel() logs an uncaught error with the promise that rejects last. Is that because there is no built in mechanism for async function's try / catch blocks to catch multiple rejections in this way?

Everything works the same in both instances if the promises resolve. Its just that when both reject, the Promise.all handles the errors, and the async function version states that one of the unhandled promise errors will crash the process in future versions of node.

Is there anyway for try / catch to handle this type of error correctly? Or do I still need to use a Promise.all inside async functions to make sure that the errors are handled correctly?

like image 213
MFave Avatar asked Oct 12 '17 14:10

MFave


People also ask

How do you handle promise rejection with await?

You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.

Does promise all work with async await?

all() with async-await. Example 1: In this example we will creating two promises inside two different functions (or methods) and in another function we will accessing them using Promise. all() along with making that function as async and promise resulting fetching will be done along with the await keyword.

Does async function return promise?

The word “async” before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically. So, async ensures that the function returns a promise, and wraps non-promises in it.

Are promises executed asynchronously?

A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed.


1 Answers

If both promises reject, then asyncParallel() logs an uncaught error with the promise that rejects last.

Yes - you created the timeoutTwo() promise but never handled its errors (like using it in an await). The await res2 was never executed due to the exception in await res1.

(Notice it's not "the promise that rejects last", but always the promise that is awaited second).

Is that because there is no built in mechanism for async function's try / catch blocks to catch multiple rejections in this way?

In sequential code, there cannot be multiple exceptions, so coming up with extra syntax to deal with them would be hard.

Do I still need to use a Promise.all inside async functions to make sure that the errors are handled correctly?

Yes, exactly that. If you want to wait for multiple promises in parallel, you should always use Promise.all. The await keyword is just sugar for the following .then() call.

You should write

async function asyncParallel() {
  try {
    let [val1, val2] = await Promise.all([timeoutOne(), timeoutTwo()]);
    console.log(`All done with ${val1} ${val2}`)
  } catch(err) {
    console.log(err)
  }
}

In both cases only the promise that returns first logs an error. I know that in some promise libraries there is a way to log all the errors (for example the "settle" method in bluebird), however, I'm not sure if there is an analogue to this method in native promises?

No, there's not. The characteristics of settle are trivial to implement yourself using then, with any values you desire:

async function asyncParallel() {
  try {
    let [stat1, stat2] = await Promise.all([
        timeoutOne().then(() => "one fulfilled", () => "one rejected"), 
        timeoutTwo().then(() => "two fulfilled", () => "two rejected")
    ]);
    console.log(`All settled with ${stat1} ${stat2}`)
  } catch(err) {
    console.log(err)
  }
}
like image 146
Bergi Avatar answered Oct 23 '22 07:10

Bergi