Skip to content
Advertisement

Why do I get an Unhandled Promise Rejection with await Promise.all

This is the general structure of my code:

JavaScript

I expect this to catch any rejections happening in asyncActions because they should be handled by Promise.all(), but somehow they are unhandled? The console shows the following:

JavaScript

Why are they not handled by Promise.all() and then caught in the catch block?

I also noticed that when I replace both the new Promise(...) with just Promise.resolve() and Promise.reject() respectively it catches the errors. Why is that? Aren’t both variants asynchronous and thus should work the same way?

Advertisement

Answer

The way we detect non-handling of promise rejections in Node.js is using a heuristic.

When a promise is rejected we give the user a chance to still attach a listener (synchronously) – if they don’t we assume it’s not handled and cause an unhandledRejection. This is because:

  • It’s impossible (as in the halting problem) to know if a user will ever attach such a handler in the future.
  • In the vast majority of cases it’s sufficient, since it’s best-practice to always immediately attach listeners.

So – you need to always add the catch listeners synchronously in order to avoid the unhandled rejections.

You can also (in your non-contrived case) just opt out by adding an empty catch listener in a fork:

JavaScript

  • Fun historical tidbit #1: we considered using GC to detect unhandledRejections – people found it more confusing.
  • Fun historical tidbit #2: Chrome does the same thing but actually removes the messages retrospectively if you add a listener.
  • Fun historical tidbit #3: This heuristic originated in bluebird in 2013. I talk about it here.
Advertisement