74

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?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
hg_git
  • 2,884
  • 6
  • 24
  • 43
  • 1
    async functions always return a promise. getResult is waiting for myFunc to resolve. then it returns the value in a promise. – tumelo Jun 15 '21 at 20:07

6 Answers6

48

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()))()
Ben Fortune
  • 31,623
  • 10
  • 79
  • 80
  • 2
    why does last async (the one with IIFE) becomes synchronous but not my `getResult()` method? – hg_git Aug 25 '17 at 08:13
  • 1
    @hg_git Because it's wrapped in an async IIFE, with an await call. It doesn't become synchronous, it's just syntactical sugar. – Ben Fortune Aug 25 '17 at 08:14
  • 2
    my `getResult()` has `async` keyword as well, and an `await` call as well.. – hg_git Aug 25 '17 at 08:16
  • what's the difference b/w two? – hg_git Aug 25 '17 at 08:17
  • @hg_git But you're getting the result of `getResult()`, it's still a promise. – Ben Fortune Aug 25 '17 at 08:18
  • 7
    @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. – Felix Kling Aug 25 '17 at 15:23
  • 1
    This is not clear. – Ben Racicot Oct 06 '22 at 14:54
  • 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. – Beraben Systems Jul 11 '23 at 02:09
43

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());
}
Nikkorian
  • 770
  • 4
  • 10
Pim_nr_47
  • 983
  • 8
  • 10
  • Nice explanation! You can se a running example here: https://codepen.io/juanmamenendez15/pen/YzPqeKj?editors=0012 – Juanma Menendez Dec 11 '19 at 05:54
  • @JuanmaMenendez your example does not help because a long-running process like `sleep` is missing! – Timo Sep 01 '22 at 20:31
  • 1
    the last example the keyword `function` is missing. You should write a full example, the calling of callingFunc is missing. – Timo Sep 01 '22 at 20:49
  • 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. – Ben Racicot Oct 06 '22 at 14:58
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

Nassim
  • 2,879
  • 2
  • 37
  • 39
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.

Nikkorian
  • 770
  • 4
  • 10
1

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

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();
Barathi
  • 1
  • 1