skip to Main Content

I haven’t touched JS for a while. Can someone explain why am I getting such an unexpected result?

My goal is to return a result from an async function. (Or, in other words, to convert an asynchronous function into a synchronous one and return the result from it.)

Let me show it in code:

<!DOCTYPE html>
<html>
<head>
</head>

<script>
function test1()
{
    return new Promise((resolve, reject) => {

        setTimeout(() => {
            console.log("Timer fires");
            resolve("Done");
        }
        , 2000);

    });
}

async function test2()
{
    console.log("Before promise");
    let result = await test1();
    console.log("After promise");

    console.log("result=" + result);
    return result;
}
</script>

<body>
    <script>
        let final_result = test2();
        console.log("Got final result=" + final_result);
    </script>

</body>
</html>

My assumption was that when I call my test2 it will wait for the result of test1, so I would get in the log:

Before promise
Timer fires
After promise
result=Done
Got final result=Done

But what I’m getting is this:

Before promise
Got final result=[object Promise]
Timer fires
After promise
result=Done

It’s almost like let result = await test1(); executes twice. Once it returns a Promise right away and somehow skips all the console.logs that follow it, and then after the timer fires, it does execute all those console.log statements.

I’m obviously missing something in the code flow of this latest JS.

2

Answers


  1. Let me clarify the order of execution for you:

    1. let final_result = test2(); is run, execution moves to test2.

    2. 'Before promise' is logged in the body of test2.

    3. let result = await test1(); is run, the execution of the function halts until the promise returned from test1 resolves.

    4. 'Got final result=[object Promise]' is logged, where [object Promise] is the pending promise returned from the test2 function.

    5. 'Timer fires' is logged after the 2000ms timer in test1 completes.

    6. 'After promise' and 'result=Done' are logged in body of test2.

    I’m pretty sure that the order of step/log line 2 and step 4 appearing is not fully deterministic, but can’t find a reference for that.

    If you want to prevent the top-level script from execution before test2 has finished, you would need to await it as well (see @asportnoy’s answer), or chain a then from it:

    test2().then((final_result) => console.log("Got final result=" + final_result));
    
    Login or Signup to reply.
  2. You need to add an await in the second script tag, otherwise it will not wait for test2() to complete before continuing. Something like this:

    <script type="module">
        let final_result = await test2(); // await added here
        console.log("Got final result=" + final_result);
    </script>
    

    Note that the type="module" is required in order to use top-level await. If you don’t want to use that, you can use an IIFE instead:

    <script>
        (async () => {
            let final_result = await test2(); // await added here
            console.log("Got final result=" + final_result);
        })();
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search