31

Just going through this tutorial, and it baffles me to understand why await only works in async function.

From the tutorial:

As said, await only works inside async function.

From my understanding, async wraps the function return object into a Promise, so the caller can use .then()

async function f() {
  return 1;
}

f().then(alert); // 1

And await just waits for the promise to settle within the async function.

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // wait till the promise resolves (*)

  alert(result); // "done!"
}

f();

It seems to me their usage are not related, could someone please explain?

11
  • 7
    That's just the way the feature was designed. The only reason to tag a function as async is because it uses await inside it. Recall that the language has to continue to work completely as normal with code written in 2003. Commented Apr 3, 2018 at 23:48
  • And your surmise about what async does is not really right. The caller can also itself use await. Basically async and await provide a way to completely re-work the pattern of explicit Promise use. Commented Apr 3, 2018 at 23:49
  • 2
    What happens when f() returns result? The calling function is going to get a return value right away — it doesn't know that you are using await inside f(). With an async function the return will be a promise. Commented Apr 3, 2018 at 23:49
  • 1
    Copied and pasted from c#. Syntactical sugar coating over async/promises. The wondering is same when I first saw it. Commented Apr 3, 2018 at 23:56
  • 3
    To OP's point, it seems like async is redundant and parser effort could flag a function async without human effort (if is has await), but maybe that's slow or won't support eval or some has other gotcha. Commented Apr 4, 2018 at 0:04

2 Answers 2

21

Code becomes asynchronous on await - we wouldn't know what to return

What await does in addition to waiting for the promise to resolve is that it immediately1 returns the code execution to the caller. All code inside the function after await is asynchronous.

  • async is syntatic sugar for returning a promise.
  • If you don't want to return a promise at await, what would be the sane alternative in an asynchronous code?

Let's look at the following erroneous code to see the problem of the return value:

function f() {
  // Execution becomes asynchronous after the next line, what do we want to return to the caller?
  let result = await myPromise;

  // No point returning string in async code since the caller has already moved forward.
  return "function finished";
}

We could instead ask another question: why don't we have a synchronous version of await that wouldn't change the code to asynchronous?

My take on that is that for many good reasons making asynchronous code synchronous has been made difficult by design. For example, it would make it too easy for people to accidentally make their whole application to freeze when waiting for an asynchronous function to return.


To further illustrate the runtime order with async and await:

async function f() {
    
  for(var i = 0; i < 1000000; i++); // create some synchronous delay

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  console.log("message inside f before returning, still synchronous, i = " + i);

  // let's await and at the same time return the promise to the caller
  let result = await promise;
  console.log("message inside f after await, asynchronous now");

  console.log(result); // "done!"

  return "function finished";
}

let myresult = f();
console.log("message outside f, immediately after calling f");

The console log output is:

message inside f before returning, still synchronous, i = 1000000 
message message outside f, immediately after calling f 
message inside f after await, asynchronous now 
done!

1 Correction about the immediateness: it returns the code execution to the caller once it gets a promise from the thing that's being awaited. This happens either when somewhere higher up in the call chain a promise is explicitly awaited or an awaited async function returns.

Thus

const xxx = async () => { /* when this returns, the call chain becomes async */ }
const xx = async () => {
    /* synchronous delay can be created here to make a point */
    console.log("inside before");
    await xxx();
    console.log("inside after")
}
const x = async () => await xx();
x();
console.log("outside");

prints

inside before
outside
inside after
Sign up to request clarification or add additional context in comments.

1 Comment

"What await does in addition to waiting for the promise to resolve is that it immediately returns the code execution to the caller." The first sentence helped me a lot.
1

async and await are both meta keywords that allow asynchronous code to be written in a way that looks synchronous. An async function tells the compiler ahead of time that the function will be returning a Promise and will not have a value resolved right away. To use await and not block the thread async must be used.

async function f() {
    return await fetch('/api/endpoint');
}

is equivalent to

function f() {
    return new Promise((resolve,reject) => {
        return fetch('/api/endpoint')
        .then(resolve);
    });
}

1 Comment

this doesn't answer the OP's question, though

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.