Await vs return vs return await

There are some differences between await,return, and return await when writing asynchronous functions, and it is important to choose the right way from them. Let’s start with the following asynchronous function:

async function waitAndMaybeReject(){
    // Wait 1 second
    await new Promise(resolve= > setTimeout(resolve, 1000));
    Flip a coin
    const isHeads = Boolean(Math.round(Math.random()));
    if(isHeads) return 'yay';
    throw Error('Boo! ');
}
Copy the code

The above function waits one second to return a promise, and then has a 50% chance of successfully returning yay or raising an error. Let’s use it in a few slightly different ways.

If you want to try to verify the result by running the following code, create async function to call foo with await.

Direct call

async function foo() {
    try{
        waitAndMaybeReject();
    }catch(e){
        return 'caught'; }}Copy the code

Here, if foo is called, the return promise is always resolved, always undefined, and there is no waiting. Since we have no await, or return the result of waitAndMaybeReject (), we cannot react to it in any way. Code like this is usually wrong.

Awaiting

async function foo(){
    try{
        await waitAndMaybeReject();
    }catch(e){
        return 'caught'; }}Copy the code

Here, if foo is called, the return promise will always wait one second and then result in either an Resolved state with a value of undefined or an resolved state with a value of “Caught”. Since we wait on the return of waitAndMaybeReject(), its rejection is returned and thrown, and the catch block is executed. However, if waitAndMaybeReject() executes without an error, we still can’t do anything with its return value.

Returning

async function foo() {
    try {
        return waitAndMaybeReject();
    }
    catch (e) {
        return 'caught'; }}Copy the code

At this point, if foo is called, the return promise will always wait 1 second and then result in either a resolved state with a value of “yaa” or a reject state, throwing an Error(‘Boo! ‘). With the return waitAndMaybeReject() line, we pass its return directly, so our catch block never executes.

Return-awaiting

If you want to get resolved state with the correct return value in the try block and catch the exception in the catch, the right choice is return await.

async function foo() {
    try {
        return await waitAndMaybeReject();
    }
    catch (e) {
        return 'caught'; }}Copy the code

Here, if foo is called, the return promise will always wait one second, and then the result will either be resolved with a value of “yay” or resolved with a value of “Caught” because we’re waiting for the result of waitAndMaybeReject(), So its exception rejecttion will be returned and thrown, and the catch block will execute. If waitAndMaybeReject() executes without error, its result is returned.

If you’re still confused by the above, it might make sense to break the code into two steps:

async function foo() {
  try {
    // Wait for the result of waitAndMaybeReject() to resolve,
    // Assign fullfill to fullfilledValue:
    const fulfilledValue = await waitAndMaybeReject();
    // If waitAndMaybeReject() reject,
    // Our code throws an exception and goes into the logic of the catch block.
    // Otherwise, the code will continue to run the following statement:
    return fulfilledValue;
  }
  catch (e) {
    return 'caught'; }}Copy the code

Note: It is superfluous to have a return await inside a block other than a try/catch (as mentioned above, just return), and even Eslint has rules to detect this, but Eslint allows this inside a try/catch block.