1

When I create an async function in node and use await, I'm making the execution waits for a promise resolution (that can be a resolve or a rejection), what I do is put an await promise inside a try/catch block and throw an error in case of a promise rejection. The problem is, when I call this async function inside a try/catch block to catch the error in case of it, I get an UnhandledPromiseRejectionWarning. But the whole point of using await isn't waiting for the promise to resolve and return it's result? It seems like my async function is returning a promise.

Example - The code an UnhandledPromiseRejectionWarning:

let test = async () => {
   let promise = new Promise((resolve, reject) => {
      if(true) reject("reject!");
      else resolve("resolve!");
   });
   try{
      let result = await promise;
   }
   catch(error) {
      console.log("promise error =", error);
      throw error;
   }
}

let main = () => {
   try {
      test();
   }
   catch(error){
      console.log("error in main() =", error);
   }
}

console.log("Starting test");
main();
ekkis
  • 9,804
  • 13
  • 55
  • 105
Henrique Borges
  • 479
  • 6
  • 15
  • Possible duplicate of [NodeJS UnhandledPromiseRejectionWarning](https://stackoverflow.com/questions/39716569/nodejs-unhandledpromiserejectionwarning) – Ele Mar 25 '18 at 01:36

2 Answers2

7

async functions always return promises. In fact, they always return native promises (even if you returned a bluebird or a constant). The point of async/await is to reduce the version of .then callback hell. Your program will still have to have at least one .catch in the main function to handle any errors that get to the top.

It is really nice for sequential async calls, e.g.;

async function a() { /* do some network call, return a promise */ }

async function b(aResult) { /* do some network call, return a promise */ }

async function c() {
   const firstRes = (await (a() /* promise */) /* not promise */);
   const secondRes = await b(firstRes/* still not a promise*/);
}

You cannot await something without being inside a function. Usually this means your main function, or init or whatever you call it, is not async. This means it cannot call await and must use .catch to handle any errors or else they will be unhandled rejections. At some point in the node versions, these will start taking out your node process.

Think about async as returning a native promise - no matter what - and await as unwrapping a promise "synchronously".

  • note async functions return native promises, which do not resolve or reject synchronously:

    Promise.resolve(2).then(r => console.log(r)); console.log(3); // 3 printed before 2
    Promise.reject(new Error('2)).catch(e => console.log(e.message)); console.log(3); // 3 before 2
    
  • async functions return sync errors as rejected promises.

    async function a() { throw new Error('test error'); }
    
    // the following are true if a is defined this way too
    async function a() { return Promise.reject(new Error('test error')); }
    
    /* won't work */ try { a() } catch(e) { /* will not run */ }
    
    /* will work */ try { await a() } catch (e) { /* will run */ }
    
    /* will work */ a().catch(e => /* will run */)
    
    /* won't _always_ work */ try { return a(); } catch(e) { /* will not usually run, depends on your promise unwrapping behavior */ }
    
Catalyst
  • 3,143
  • 16
  • 20
  • Ok, but I have already used the try/catch block inside function test to handle the promise rejection. Why can't I throw an error and get this error at function main? – Henrique Borges Mar 25 '18 at 01:41
  • no matter what you do, an async function returns a promise. You can literally do `async () => 2` and that's still going to return a promise. The nice thing about async/await is that it normalizes all returns, regardless of what happens inside the async function. This way we don't have to deal with functions that sometimes throw synchronously, and sometimes return a rejected promise. – Catalyst Mar 25 '18 at 01:43
  • 1
    @HenriqueBorges to be clear - test is an async function, it will never throw traditionally - something has to await it or use .catch – Catalyst Mar 25 '18 at 01:48
  • Sorry if I'm being stubborn, it just doesn't make sense to me. Async functions will always return promises. Why in function test I can get a promise rejection with a try/catch block and in function main I can't get the promise rejection with a try/catch block? – Henrique Borges Mar 25 '18 at 01:48
  • @HenriqueBorges no worries. The code is stubborn too. in function test you `let result = await promise;` using `await`. using await unwraps the promise and if it contains a rejection, then the try/catch will be able to catch it. The problem you're running into is that you only get that sugar if the function you are inside of, is async (and therefore returns a promise). – Catalyst Mar 25 '18 at 01:52
  • 1
    Oh I got it now, thanks a lot for the patience and the explanation @Catalyst. I'm really grateful. Now back to the code :) – Henrique Borges Mar 25 '18 at 02:00
  • @HenriqueBorges Updated to have more examples for anyone else who comes along so hopefully they won't have to read through the comments in depth. – Catalyst Mar 25 '18 at 02:21
  • 1
    Maybe worth another example, that `try { return a(); } catch(e) {}` will not work. I got caught by this - when converting Promise.then.catch patterns into async / await functions it's tempting to not do an await on the final promise in the function because it's superfluous. But if you're also enclosing that in a try / catch block, the catch will never work, because the lack of await means the exception / reject inside the block is never 'unwrapped'. `return await a()` will get caught, `return a()` won't. – MrCranky Jan 09 '20 at 09:54
  • @MrCranky agreed, the missing await before return is a super huge gotcha in most code. I have seen some bad transpilation/shim behavior (do not remember where) that does behave such that it gets caught though. – Catalyst Jan 10 '20 at 05:04
-1

Main must be an async function to catch async errors

// wont work
let main = () =>{
    try{
        test();
    }catch(error){
        console.log("error in main() =", error);
    }
}

// will work
let main = async () =>{
    try{
        test();
    }catch(error){
        console.log("error in main() =", error);
    }
}
Simon
  • 1,280
  • 3
  • 10
  • 21