1

MDN documentation states that

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

Therefore, what am I doing wrong, considering that await expects a resolved promise?

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
    setTimeout(() => {
        console.log("bar");
        return;
    }, 1000);    
}

function zoo(){
    console.log("zoo");    
}

According to what I (wrongly) understood, it should first log bar and then zoo, but it's logging the other way around.

EDIT: Now, thanks to clarifications from @Matt Morgan, I understand the mistake, as the bar() function returns undefined. Nonetheless, I thought that by calling an async function per se alone would make the function return immediately an unresolved promise and that such promise would be resolved when the async function would return any value (even undefined). But I now realise one really needs to return a promise oneself by declaring it with return Promise statement. I think the MDN article for async, which I read it all, is a bit confusing on this topic (just my opinion).

Therefore I could simply amend my bar() function to:

function bar(){
    return new Promise(function(resolve){
        setTimeout(() => {
            console.log("bar");
            resolve();
        }, 1000);
    });
}
Matt Morgan
  • 3,618
  • 3
  • 14
  • 27
João Pimentel Ferreira
  • 9,166
  • 6
  • 52
  • 67
  • In the link you just posted, go look at the code for `resolveAfter2Seconds`, then compare it to your bar(). Notice something? –  Feb 05 '18 at 20:20
  • @Will, I read all that article plus the example, but I don't understand why you need to return a `Promise` if the async function already returns a `Promise` by simply running `return;`. The documentation says "when an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value" – João Pimentel Ferreira Feb 05 '18 at 20:47
  • 1
    @Will Therefore "I know how to make it work", I simply don't understand why it does work like that `:)` – João Pimentel Ferreira Feb 05 '18 at 21:08
  • Interesting. Returns are automatically wrapped in a promise. That sure cuts down on the boilerplate. Neat. –  Feb 06 '18 at 15:01
  • *”Nonetheless, I thought that by calling an async function per se alone would make the function return immediately an unresolved promise and that such promise would be resolved when the async function would return any value (even undefined)”* that’s exactly how async functions work. However, `bar` does immedeatly return `undefined`. It does not (and cannot) wait for the timeout to finish. For that to work you have to construct the promise yourself. – Felix Kling Feb 07 '18 at 16:01
  • @Felix Kling 1) why `bar` returns immediately if I just `return` after the timeout is finished? 2) if it cannot wait for the timeout to finish, why do I need an `async` function? Just to embed `await` calls? – João Pimentel Ferreira Feb 07 '18 at 17:55
  • 2
    1) `return` doesn’t cross function boundaries. You are returning inside the callback only. The function will return to its caller. But you are not the caller of the callback, the browser will call the function when the timeout is up. The cal to `setTimeout` itself returns immediately. It’s not blocking. 2) If you explicitly create and return a promise, then you don’t need async. If you want to be able to use await inside a function, you need async. – Felix Kling Feb 08 '18 at 00:08

1 Answers1

3

bar() is setting the timeout and returning undefined, which is not the same as immediately running the logging statement that runs when the timeout finishes.

So, zoo() runs, and then when the timeout finishes (1000ms later), you see "bar" in the console.

Here's a modified example without the timeout:

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
   console.log("bar");   
}

function zoo(){
    console.log("zoo");    
}

With no setTimeout, you get the order of execution you expect.

A second example, where you have a delay() function that wraps setTimeout in a promise:

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
   await delay();
   console.log("bar");   
}

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

function zoo(){
    console.log("zoo");    
}

The final snippet does wait for the resolution of the promise, so you see bar, then foo.

The above delay was taken from https://stackoverflow.com/a/39538518/3084820

Here's a final example, where bar() returns "bar". This means it gets wrapped in a promise by the async declaration, and resolved by the await inside foo() Again, you see what you expected to see.

foo();

async function foo(){
    console.log(await bar());
    zoo();
}

async function bar(){
    return 'bar';   
}

function zoo(){
    console.log("zoo");    
}

It's important to understand the your original example does not return the value bar. It returns undefined, and if you change your original code to log out the returned value of bar(), you would see three things in the console:

  1. The immediately returned promise form bar() that resolves to undefined.
  2. The value zoo from the zoo() function.
  3. Finally, after 1000ms elapsed, you would see the bar logged from the setTimeout that got shoved on the queue.

Try it, see what you get.

Matt Morgan
  • 3,618
  • 3
  • 14
  • 27
  • thanks for replying, I understand your code perfectly. In your first example, there is no need to `async`, it is merely a sequence of commands; in your second example which I have already used, you return a `Promise`, but I don't understand why you need to return a `Promise` if the async function already returns a `Promise` by simply running `return` – João Pimentel Ferreira Feb 05 '18 at 20:45
  • 1
    The function `bar` returns `undefined`. In your original code, you're setting a timeout, which really doesn't have anything to do with promises. In order to test the async behavior, you need to have a promise that resolves when the timeout completes, which is why I included the second example. – Matt Morgan Feb 05 '18 at 20:48
  • ok, but MDN documentation says that "when an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value". That made confused! – João Pimentel Ferreira Feb 05 '18 at 20:49
  • 1
    And the promise is immediately resolved with a value of `undefined`. I think you're getting confused in thinking that the resolution of the promise will result in the `console.log` statement getting executed. – Matt Morgan Feb 05 '18 at 20:50
  • but if there is a promise (I don't care for the purpose of the code, what value will be), shouldn't `await` wait for the resolution of the `promise`? Why is immediately resolved if I just `return` later? – João Pimentel Ferreira Feb 05 '18 at 20:52
  • 1
    `setTimeout` leverages a browser API that inserts the wrapped function into the queue after a certain amount of time. It does not create a promise. Your function sets the timeout, then returns `undefined`, and the promise resolves with a value of `undefined`. – Matt Morgan Feb 05 '18 at 20:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164578/discussion-between-matt-morgan-and-joao-pimentel-ferreira). – Matt Morgan Feb 05 '18 at 20:55
  • I marked your solution as The solution, but I still don't understand why the `async` function doesn't return a returned promise when it simply returns. The documentation says "When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value." Apologies if I'm being persistent! – João Pimentel Ferreira Feb 05 '18 at 21:05
  • Therefore "I know how to make it work", I simply don't understand why it does work like that `:)` – João Pimentel Ferreira Feb 05 '18 at 21:07
  • Thanks again! Now I see that it simply returns `undefined`. I thought that by calling `async` _per se_ would make the function return immediately an unresolved promise and that such promise would be resolved when the `async` function would return any value (even undefined). But I now I see you really need to return a promise yourself by declaring it with `return Promise` statement. I think the MDN article for `async`, which I read it all, is a bit confusing on this topic (just my opinion). Anyway, thanks a lot! – João Pimentel Ferreira Feb 05 '18 at 21:31