Search code examples
javascriptnode.jspromisees6-promise

Do browsers still swallow unhandled rejected promises silently? What about Node?


There is a lot of advice out there that advises you to ensure that you don't let any rejected promises go unhandled. If you don't, the advices cautions, the errors will never be noticed, and will be completely swallowed. Nothing will be printed to the console.

This advice seems to be out-of-date. Modern browsers and modern versions of Node do seem to print warnings when rejected promises are unhandled. Take this code:

async function thisIsGoingToFail() {
  await Promise.reject();
  console.log('this should not print, as the line above should error');
}

async function main() {
  await thisIsGoingToFail();
}

main();

If you run this in Node, you will get:

(node:20760) UnhandledPromiseRejectionWarning: undefined
(node:20760) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:20760) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The standard advice is that the last line in the source code should look like this instead, to avoid swallowed errors:

main().catch(err => { console.err(err) });

But it doesn't look like that like was needed. So here are my questions:

  • Is it true that modern browsers and modern Node will always show a warning for unhandled rejected promises? What version numbers of the implementations support this?
    • (Note that a browser doesn't have to support the event unhandledrejection in order to print a warning when a promise is not handled.)
  • Do we need to make sure to have top-level catch functions as you would often get advised to do, or is it just as useful to let the implementation show a warning?

Solution

  • Most modern implementations do print warnings to the console when rejected promises are unhandled, but not all:

    • Node.js Since version 6.6.0 (see changelog):

      promises: Unhandled rejections now emit a process warning after the first tick. (Benjamin Gruenbaum) #8223

      In future versions, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

    • Firefox does show a warning, but I am unable to find which was the first version that does this. MDN docs say this:

      We adopt the following strategy: if a promise is rejected at the time of its garbage-collection and if the promise is at the end of a promise chain (i.e. thatPromise.then has never been called), then we print a warning.

      Note: This warning is generated some time after the error occurred, and may provide less information about the error location. It generally indicates the need to insert a proper error handler. When a proper rejection handler is used, it effectively replaces this delayed reporting.

    • Chrome does show a warning, but again I can't find documentation indicating which is the first version to do this. Chrome >= 49 goes a step further, and has support for the unhandledrejection event, which lets you add a custom event handler to deal with all unhandled rejected promises.

    • Safari also shows a warning, and since version >= 11, also supports the unhandledrejection event.

    • Edge seems to support it as of Chakracore 1.10, based on this GitHub issue in Microsoft/ChakraCore.

    So it looks like most modern browsers (except Edge) do show warnings for unhandled rejected, but there isn't clear documentation guaranteeing that a warning will be shown, or that it will be shown promptly. Handling the unhandledrejection event seems like a good idea for the browsers that support it.


    tldr; As of writing, it seems like the best practise is to still have top-level rejected promise handlers with catch (to avoid swallowed errors in Edge and terminated processes in Node). For Chrome and Safari, it might be useful to take advantage of unhandledrejected event.