async function - await not waiting for promise
Asked Answered
T

6

76

I'm trying to learn async-await. In this code -

const myFun = () => {
    let state = false;

    setTimeout(() => {state = true}, 2000);

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(state) {
                resolve('State is true');
            } else {
                reject('State is false');
            }
        }, 3000);
    });
}

const getResult = async () => {
    return await myFun();
}

console.log(getResult());

why am I getting output as -

Promise { <pending> }

Instead of some value? Shouldn't the getResult() function wait for myFun() function resolve it's promise value?

Turco answered 25/8, 2017 at 7:39 Comment(1)
async functions always return a promise. getResult is waiting for myFunc to resolve. then it returns the value in a promise.Dipterous
S
49

If you're using async/await, all your calls have to use Promises or async/await. You can't just magically get an async result from a sync call.

Your final call needs to be:

getResult().then(response => console.log(response));

Or something like:

(async () => console.log(await getResult()))()
Sherer answered 25/8, 2017 at 7:46 Comment(8)
why does last async (the one with IIFE) becomes synchronous but not my getResult() method?Turco
@Turco Because it's wrapped in an async IIFE, with an await call. It doesn't become synchronous, it's just syntactical sugar.Sherer
my getResult() has async keyword as well, and an await call as well..Turco
what's the difference b/w two?Turco
@Turco But you're getting the result of getResult(), it's still a promise.Sherer
@hg_git: async functions return promises. await "magically" unwraps a promise before the following code is executed. The code looks synchronous, but it is not executed synchronously.Divider
This is not clear.Runin
There is a subtle difference between @FelixKling and this answer. For the intended wait to work the console print must happen within async function. Code executed in non-async parent (i.e. console.log() in the op's post) will not wait for any async results.Capablanca
E
47

What you need to understand is that async/await does not make your code run synchronously, but let's you write it as if it is:

In short: The function with async in front of it is literally executed asynchronously, hence the keyword "async". And the "await" keyword wil make that line that uses it inside this async function wait for a promise during its execution. So although the line waits, the whole function is still run asynchronously, unless the caller of that function also 'awaits'...

More elaborately explained: When you put async in front of a function, what is actually does is make it return a promise with whatever that function returns inside it. The function runs asynchronously and when the return statement is executed the promise resolves the returning value.

Meaning, in your code:

const getResult = async () => {
    return await myFun();
}

The function "getResult()" will return a Promise which will resolve once it has finished executing. So the lines inside the getResult() function are run asynchronously, unless you tell the function calling getResult() to 'await' for it as well. Inside the getResult() function you may say it must await the result, which makes the execution of getResult() wait for it to resolve the promise, but the caller of getResult() will not wait unless you also tell the caller to 'await'.

So a solution would be calling either:

getResult().then(result=>{console.log(result)})

Or when using in another function you can simply use 'await' again

async function callingFunction(){
    console.log(await(getResult());
}
Exanthema answered 30/5, 2019 at 12:39 Comment(4)
Nice explanation! You can se a running example here: codepen.io/juanmamenendez15/pen/YzPqeKj?editors=0012Poi
@JuanmaMenendez your example does not help because a long-running process like sleep is missing!Kopp
the last example the keyword function is missing. You should write a full example, the calling of callingFunc is missing.Kopp
But you can't use await unless that function returns a promise which negates this usefulness. We could just use .then/catch for the same effect.Runin
L
3

This is my routine dealing with await and async using a Promise with resolve and reject mechanism

    // step 1 create a promise inside a function
    function longwork()
    {
        p =  new Promise(function (resolve, reject) {
            result = 1111111111111 // long work here ; 

            if(result == "good"){
                resolve(result);
            }
            else
            {
                reject("error ...etc")
            } 
        })

        return p
    }

    // step 2 call that function inside an async function (I call it main)and use await before it
    async function main()
    {
         final_result = await longwork();
         //..

    }  

    //step 3 call the async function that calls the long work function
    main().catch((error)=>{console.log(error);})

Hope that saves someone valuable hours

Lumbricoid answered 21/5, 2020 at 13:21 Comment(0)
N
2

What hasn't been mentioned in this discussion are the use-case implications of the behaviour. The key thing, as I see it, is to consider what you are planning to do with the output from the top level, truly asynchronous function, and where you are planning to do that.

If you are planning to consume the output immediately, i.e. within the "async" function that is awaiting the return of the top level asynchronous function, and what you do with the output has no implication for other functions deeper in the call stack, then it does not matter that the deeper functions have moved on. But if the output is needed deeper in the call stack, then you need use "async" functions making await calls all the way down the stack to that point. Once you reach a point in the call stack where the function does not care about the asynchronous output, then you can stop using async functions.

For example, in the following code, function B uses the stuff returned from function A so is declared "async" and awaits A(). Function C() calls B(), is returned a Promise, but can move straight on before that promise is resolved because it is not interested in A()'s stuff, nor what's done with it. So C does not need to be declared as async, nor await B().

function A() {
    return new Promise((resolve, reject) => {
        //do something slow
        resolve (astuff)
    }
}
async function B() {
    var bstuff = await A();
    dosomethingwith(bstuff);
    return;
}
function C() {
    B();
    dontwaitmoveon();
    ...
    return;
}

In this next example, C() does use A()'s stuff, so needs to wait for it. C() must be declared "async" and await B(). However D() does not care about A()'s stuff, nor what's done with it, so moves on once C() returns its promise.

function A() {
    return new Promise((resolve, reject) => {
        //do something slow
        resolve (astuff)
    }
}

async function B() {
    var bstuff = await A();
    dosomething();
    return bstuff;
}

async function C() {
    var cstuff = await B();
    dosomethingwith(cstuff);
    ...
    return;
}

function D() {
    C();
    dontwaitmoveon();
    ...
    return;
}

Since figuring this out, I have tried to design my code so the stuff returned by the asynchronous function is consumed as close as possible to the source.

Ninny answered 7/10, 2022 at 0:22 Comment(0)
K
0

Though your "getResult" function is async and you have rightly made an await call of myFun, look at the place where you call the getResult function, it is outside any async functions so it runs synchronously.

So since getResult called from a synchronous point of view, as soon as it is called, Javascript synchronously gets whatever result is available at the moment, which is a promise.

So returns from an async function cannot be forced to await(very important), since they are synchronously tied to the place of origin of the call.

To get what you want you can run the below,

async function getResult() {
  const result = await myFun();
  console.log(result);
//see no returns here
}
getResult();
Kohlrabi answered 5/7, 2022 at 17:5 Comment(0)
T
0

BTW, await doesn't work inside of .map(), .filter(), .forEach(), etc... You have to use a plain vanilla for() loop.

Talishatalisman answered 1/5, 2023 at 18:19 Comment(2)
While true, this does not appear to answer the questionHeracliteanism
It does if you're stumped (like I was) why await wasn't working inside of a .map() loop!Talishatalisman

© 2022 - 2024 — McMap. All rights reserved.