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?

James Lin
  • 25,028
  • 36
  • 133
  • 233
  • 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. – Pointy Apr 03 '18 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. – Pointy Apr 03 '18 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. – Mark Apr 03 '18 at 23:49
  • @Pointy: I was thinking that too, but wouldn't both keywords throw in IE6? – dandavis Apr 03 '18 at 23:51
  • Have you read the [documentation of `async`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)? The answer is there. – axiac Apr 03 '18 at 23:52
  • I am just going through the tutorial linked in the question, it says "As said, await only works inside async function." – James Lin Apr 03 '18 at 23:53
  • @dandavis Most people use transpilers (babel) if they intend to support older platforms. – Maximilian Burszley Apr 03 '18 at 23:54
  • @dandavis well a function declared as `async` is a different sort of animal than one that isn't; so yes that's the point. The runtime has to know whether a function reference might be `async`, and an old runtime would just throw an error. – Pointy Apr 03 '18 at 23:56
  • 1
    Copied and pasted from c#. Syntactical sugar coating over async/promises. The wondering is same when I first saw it. – int-i Apr 03 '18 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. – dandavis Apr 04 '18 at 00:04
  • In my understanding, if **await** is permitted outside async scope, one could freeze the whole program by making all asynchronous operations(for eg. http request) to be waited. As a result, the user interface doesn't respond. This is not the design pattern of javascript. So, by limiting await in async scope we can ensure that the program will not be blocked at all. Btw, this feature is available in many other programming languages. – Abdulwehab Jan 19 '23 at 04:46

2 Answers2

17

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 immediately 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!
Simo Kivistö
  • 4,247
  • 3
  • 38
  • 42
  • 1
    "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. – Tao Zhang Oct 03 '21 at 01:49
3

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);
    });
}
Meghan
  • 1,215
  • 11
  • 17