DEV Community

Cover image for 'return await' in a try/catch
Daniel Bellmas
Daniel Bellmas

Posted on

'return await' in a try/catch

This might sound weird to you, the first thing that comes to mind is: 'Eslint will tell me to remove the await, there is no need for an await after a return'

But the case is a different when we wrap our promise in a try/catch.

async function foo() {
  try {
    return await waitAndMaybeReject();
  } catch (e) {
    return 'caught';
  }
}
Enter fullscreen mode Exit fullscreen mode

This also applies when we don't need to return resolved value of the promise, we always need to await it if the returned promise is inside a try/catch and we want to catch the error.

By returning a promise, we’re deferring its result, so our catch block never runs.
This also happens, when not awaiting a promise (regardless if we return or not).

Only outside of try/catch blocks, return await is redundant. There’s even an Eslint rule, but this rule allows a return if it's in try/catch.


Bonus🔥

If you want to print something after a return statement, instead of temporaraly creating a variable, printing and then returning, like this:

async function foo() {
  const res = await fetch();
  console.log('after fetch')
  return res;
} 
Enter fullscreen mode Exit fullscreen mode

We can wrap the return with a try and finallly (unless you need to print the resolved value of the promise of course), like so:

async function foo() {
  try {
    return await fetch();
  } finally {
    console.log('after fetch')
  }
} 
Enter fullscreen mode Exit fullscreen mode

Top comments (17)

Collapse
 
joshuakb2 profile image
Joshua Baker

In my opinion, you should almost never return a promise from an async function. You should return await instead. Not only does that cover this case naturally, it also improves async stack traces for debugging.

Collapse
 
momander profile image
Martin Omander

Returning a promise from an async function is still needed if you want to run several operations in parallel, isn't it? That optimization comes up fairly frequently in my code when I tune its performance.

Collapse
 
hilleer profile image
Daniel Hillmann • Edited

Why can you not run multiple functions in parallel if they're awaited? 🤔

async function getWebpage(url) {
  try {
    const { data: webpage } = await axios.get(url);

    return webpage;
  } catch (err) {
     // a request failed
     // will make Promise.all throw
  }
}
const results = await Promise.all([
  getWebpage('https://google.com'),
  getWebpage('https://facebook.com')
]);
// Executed in parallel
// results[0] = google
// results[1] = facebook 
Enter fullscreen mode Exit fullscreen mode
Collapse
 
danielbellmas profile image
Daniel Bellmas • Edited

That's right, that's a good use case for returning a promise without await, thanks for the clarification 🙏

Collapse
 
manchicken profile image
Mike Stemle

An async function, definitionally and per spec, always returns a Promise.

Working proof: replit.com/@manchicken/What-async-...

Collapse
 
bmorearty profile image
Brian Morearty

I think it’s pretty common to call another async function at the end of your async function and return the result. As in the example. In that case there’s no way around it.

Collapse
 
danielbellmas profile image
Daniel Bellmas • Edited

Interesting, Are you disabling the Eslint rule then?

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

The rule definition is as follows, personally I think it's rather dangerous as the most likely effect of applying it is that exceptions end up being incorrectly handled. Now this happens because initially there is no try/catch so eslint complains about the await and then the developer removes it and later adds a try/catch block for errors - if it had been there from the start then the await would not have been linted.

Disallows unnecessary return await (no-return-await)

Inside an async function, return await is useless. Since the return value of an async function is always wrapped in Promise.resolve, return await doesn’t actually do anything except add extra time before the overarching Promise resolves or rejects. This pattern is almost certainly due to programmer ignorance of the return semantics of async functions.

Thread Thread
 
danielbellmas profile image
Daniel Bellmas

I agree, thanks for the awesome explanation Mike!

Collapse
 
artxe2 profile image
Yeom suyun

Async await is more convenient than Promise chains, but I don't see why you wouldn't use Promise.catch in the example.

Collapse
 
danielbellmas profile image
Daniel Bellmas

You could, this example is pretty simple so you're right, but for more complex logic try and catch is usually more convinient

Collapse
 
momander profile image
Martin Omander

Great post! It was short, easy to understand, and easy to see the advantage of organizing the code you propose.

Collapse
 
danielbellmas profile image
Daniel Bellmas

Thank you Martin! I appreciate it :)

Collapse
 
shameel profile image
Shameel Uddin

I could not fully understand the purpose if this blog.

Collapse
 
mickmister profile image
Michael Kochell

Yeah I don't get the pros and cons of using it. I didn't even know you could do that, and I'm unsure exactly why I would or would not want to in the context of a try block, based on reading this blog post.

Collapse
 
danielbellmas profile image
Daniel Bellmas

The main pro is being able to catch the error. I don’t see a con.
This is a specific use case for when you return a promise

Collapse
 
danielbellmas profile image
Daniel Bellmas

To share a bug I had where I didn’t use await when I returned a promise and I couldn’t understand why my console log in the catch didn’t print