Search code examples
node.jsasync-awaitpromiseunhandled-promise-rejection

Why does delaying handling of promise rejections sometimes result in UnhandledPromiseRejectionWarning?


When I run the following code, why do I get unhandled promise rejection warning?

async function load() {
  throw new Error('error');
}

async function main() {
  const promise = load();

  await new Promise(resolve => setTimeout(resolve, 5000));

  try {
    await promise;
  } catch (e) {
    console.log('caught error', e);
  }
}

main();

This is the output:

jkim@dev-jkim test $ node index.js
(node:25276) UnhandledPromiseRejectionWarning: Error: error

Since await promise is around a try-catch, I'm confused why my try-catch isn't catching the error. I guess it's something to do with the setTimeout since the following code works:

async function load() {
  throw new Error('error');
}

async function main() {
  const promise = load();
  try {
    await promise;
  } catch (e) {
    console.log('caught error', e);
  }
}

main();
jkim@dev-jkim test $ node index.js
caught error Error: error

What is going on here? If promise rejections are not handled by the end of the current tick, does it automatically result in a unhandled promise rejection warning?

(I'm on node v10.16.3)


Solution

  • If promise rejections are not handled by the end of the current tick, does it automatically result in a unhandled promise rejection warning?

    Yes. A Promise must have a rejection handler attached to it at the moment it rejects, or the rejection will count as unhandled. If you attach the rejection handler later, such as after a

    await new Promise(resolve => setTimeout(resolve, 5000));
    

    the load Promise has rejected by the time the interpreter gets to the

    try {
      await promise;
    } catch (e) {
    

    so, although the rejection can be caught with .catch, it wasn't caught by anything at the moment of rejection, resulting in the warning.

    Best practice for this sort of thing is to always attach a rejection handler immediately - whether that means .catch, or inside a try/catch, or a Promise.all, or returning the Promise for the caller to handle.