Tân
Tân

Reputation: 1

How to await an asynchronous function?

My case:

let waiting = function () {
  return new Promise(resolve => {
    console.log('awaiting...');
    
    setTimeout(function () {
      resolve();
    }, 1000)
  });
};

let waitingAsync = async function () {
  console.log('start...');

  await waiting();

  console.log('stop...');
};

waitingAsync();
console.log('done...');

There are 2 things I don't understand in the code:

The first:

await waiting();

waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?

The second:

Why couldn't done... message be awaited after completing waitingAsync function?

And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().

If I can await waitingAsync(), done... message would be printed last.

Upvotes: 4

Views: 8135

Answers (5)

Before diving in, it's good to notice a few things.
Any reader of the code snippet

let waiting = function () {
  return new Promise(resolve => {
    console.log('awaiting...');
    setTimeout(function () { resolve(); }, 1000);
  });
};

let waitingAsync = async function () {
  console.log('start...');
  await waiting();
  console.log('stop...');
};

waitingAsync();
console.log('done...');

may be mislead to believe that the output will be

start...
awaiting...
stop...
done...

while – as you have already noted – done... gets printed before stop....

The reason is that waitingAsync(); is a call to an asynchronous function, while console.log('done...'); is just a normal sequential/synchronous statement that gets carried out right away.


Question 1:

waiting is a synchronous function (because it doesn't have async keyword) [?]

Answer:
False. The function waiting is asynchronousit returns a Promise.


Question 2:

Why couldn't done... message be awaited after completing waitingAsync function?

Answer:
Because console.log('done...') is not asynchronous.
(It does not return a Promise.)


Question 3:

And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it?

Answer:
Well, in your example waitingAsync does not return any value. - If it would return a value that you care about, then you would need to await it to get it.
(Hello world! in my Stack Snippet below.)


Question 4:

If I can await waitingAsync(), [the] done... message would be printed last [?]

Answer:
That depends on what exactly you mean. – See my Stack Snippet below! As long as the Done! message is printed within the same callback as the call await waitingAsync(), the answer is Yes!
But if you put console.log('done...?') after the call to the asynchronous function that encloses await waitingAsync() then the answer is No!


When running the snippet below, pay attention to the order of the output!
Also notice how it takes 1400 ms for Promise resolved! to show up.

function waiting () {
  return new Promise(resolve => {
    console.log('awaiting...');
    setTimeout(function () {
      resolve('Hello world!');
      console.log('Promise resolved!');
    }, 1400);
  });
}

async function waitingAsync () {
  console.log('start...');
  const toBeReturned = await waiting();
  console.log('stop...');
  return toBeReturned;
}

(async () => {
  console.log('Heads up! The next line makes an asynchronous call.');
  console.log('Result: ' + await waitingAsync());  // 'Hello world!'
  console.log('Done! This will be printed LAST! - Agreed?');
})();

console.log('done...?? This is LAST in the CODE. - I awaited \
"waitingAsync()" above. - So will this be printed at the very end??');
.as-console-wrapper { max-height: 100% !important; top: 0; }

The last asynchronous function is anonymous – without name – and gets called immediately. In fact, this is the only function that gets called directly in the snippet. The function waitingAsync is only called indirectly (by the anonymous function), and the function waiting is also called indirectly (by waitingAsync).

A take away lesson

Don't ever put sequential/synchronous code after and outside a call to an asynchronous function!

You will just confuse yourself if you do. – And even if you don't get confused, other readers of your code almost certainly will be.

Upvotes: 1

Sergeon
Sergeon

Reputation: 6788

When you await a function, if that function returns a promise, its return value will be treated as the promise then value. If the promise will reject, it will be cast to an Error. If the function call returns something ohter than a thenable, well, await then just doesn't nothing.

In the other hand, when you declare an async function, its return value will be returned as a Promise, and any Error thrown from it will be casted to a rejected Promise.

You can use await only within an async declared function.

that's just about async and awaitare, just automatic casting to promises. You don't actually need code using await and async to be really asynchronous (while, well, it's not really useful).

A quick demonstration:

//Will return the string 'Promise' if called through `await`
function getPromise(){
    return Promise.resolve('Promise');
}

//Casted to Promise.reject thrught await 
function throwError(){
    return Promise.reject('error'); 
}

function get(){
    return 'something simple'; 
}

async function getAsync() {
    var response = await getPromise();
    return response;
}
//await will cast the rejected Promise to an error
async function getErrorAsync() {
    var response = await throwError();
    return response;
}

async function simpleGet(){
    return get();
}

async function redundantGet(){
    return await get();
}

    async function catchTheError(){
      try{
          await throwError();
      }       
      catch(e){
         console.log('an error: ' + e );
      }
       return 'whatever';
 }

getAsync().then( console.log ); //Promise
getErrorAsync().catch( console.log ); //error
simpleGet().then( console.log ); //something simple
redundantGet().then( console.log ); //something simple
catchTheError(); //will log 'an error: error'.

So:

waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?

Because you can. The only thing await does is to resolve promise to real values and errors. You don't actually need the function to return a promise.

Why couldn't done... message be awaited after completing waitingAsync function?

async and await only makes your code to behave as it would be synchronous iside asyncdeclared functions. Your last console.log('done') is outside any async function, so it will be just logged before that function ends, since it is asynchronous.

And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().

Because async keyword casts values to promises -and allows to use await- and nothing more. In fact, since you can only use await inside async functions... you can't expect async functions to be called through await, you would need infinite asyncfunctions :-D.

Upvotes: 1

Holanda Junior
Holanda Junior

Reputation: 56

A async function does return a promise or, when it uses the await keyword, it must wait for a asynchronous function, being a Promise or another async function. In your code, waiting() is a function that returns a Promise. Then, await waiting() is correct (since it is waiting for a asynchronous function).

An async function is called as another basic functions is called. You may mark a function as async when you desire operate it as an asynchronous function and using the 'await' keyword. The 'done...' is printed because when you call asyncFunction(), it must await until waiting promise is finished. But the program is not stopped and go on showing done.... If you want to wait for it, maybe you can use asyncFunction().then( () => console.log('done...') )

Upvotes: 2

Estus Flask
Estus Flask

Reputation: 222369

This isn't a function but a value that it returns which is awaited with await statement.

async and normal functions aren't different to the caller. async just returns a promise without returning it explicitly when being called. The result of waitingAsync() call is a promise. The result of waiting() call is a promise, too, so it isn't 'synchronous'.

According to the spec, both promises and non-promises can be awaited. Non-promises are converted to promises with Promise.resolve().

console.log('done...') can't be awaited because it isn't called inside async function. And it doesn't have to be awaited because it doesn't return a promise but undefined. awaiting it would be possible within async function. These await usages are equal and equally useless, all they do is 1 tick delay:

async function ... {
  ...
  await console.log('done...');
}

async function ... {
  ...
  console.log('done...');
  await undefined;
}

async function ... {
  ...
  await Promise.resolve(console.log('done...'));
}

Upvotes: 5

Sergei Klykov
Sergei Klykov

Reputation: 1037

Async keyword used to specify that function will be an instance of AsyncFunction so it will return Promise.

Await is used to wait for a promise resolving inside of async function.


According to a mdn - async function can contain an await expression, that pauses the execution of the async function and waits for the passed promise's resolution, and then resumes the async function's execution and returns the resolved value.

Upvotes: 2

Related Questions