1

I have following asynchronous code example:

// Functions

function getSomePromise() {
    let a = new Promise((resolve, reject) => {    
        setTimeout(function(){
            console.log("Inside promise...");
            resolve("Success!"); 
        }, 1000);
    });

    return a;
}

async function someWrapper(i) {
    console.log('A: '+ i);
    await getSomePromise();
    console.log('B: ' + i);    
}

And two tests:

async function test1() {
    for(let i=0; i<5; i++) {
        // body copy-pasted of someWrapper function:
        console.log('A: '+ i);
        await getSomePromise();
        console.log('B: ' + i);
    }    
}

async function test2() {
    for(let i=0; i<5; i++) {
        someWrapper(i);                
    }    
}

And here are results in chrome console after run separatley test1() and test2():

Test 1               |      Test 2
---------------------------------------------
A: 0                 |      A: 0
Inside promise...    |      A: 1
B: 0                 |      A: 2
A: 1                 |      A: 3
Inside promise...    |      A: 4
B: 1                 |      Inside promise...
A: 2                 |      B: 0
Inside promise...    |      Inside promise...
B: 2                 |      B: 1
A: 3                 |      Inside promise...
Inside promise...    |      B: 2
B: 3                 |      Inside promise...
A: 4                 |      B: 3
Inside promise...    |      Inside promise...
B: 4                 |      B: 4

Question: Why when we use function someWrapper() in for-loop (test2) we get different result than wen we copy-paste this function body directly into for-loop (test1) ?

(above example is quite abstract, however "I found this behaviour" on calling ajax requests (instead console.log('A: '+ i); and console.log('B: '+ i);) which sequence are very important in my app (request A1 must be before request B0...) )

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • 3
    Your first test uses `async` and `await`, which runs the promises in order. The second test does not, so every promise is started and then they finish in order. – Sidney Feb 07 '18 at 17:52
  • @Sidney can you elaborate more - in my point of view - I use async/await inside someWrapper() and in Test2() i call in synchronous (by for loop) way this function. – Kamil Kiełczewski Feb 07 '18 at 17:56
  • In the first test's loop, `await` causes the loop to pause at the `getSomePromise()` function, until the promise has resolved. In the second test, there isn't an `await`, so JavaScript happily continues the loop after starting each promise. Making a function `async` doesn't really do anything without using `await` in the function. – Sidney Feb 07 '18 at 17:58
  • @Sidney The only one difference is that i move loop-body (in test 1) to function (test2). And in my point of view this behaviour is counterintuitive - Can you agree with that? – Kamil Kiełczewski Feb 07 '18 at 18:00
  • Another way to say it: calling the `async` function _transforms_ the necessity to wait for a value into a `Promise`. This means you could do `await someWrapper(i)` to get the same result. – JJWesterkamp Feb 07 '18 at 18:00
  • @Sidney - you was the first - so create answer (and elaborate more with including your opinion about this behaviour: intuitive or counterintuitive) and I will check it (I give you 1 day for that). – Kamil Kiełczewski Feb 07 '18 at 18:08
  • The thing to remember is that `async` functions immediately return a promise and from the caller's perspective an `await` doesn't wait for anything. – HMR Feb 08 '18 at 03:24
  • @HMR - hm... I not understand - in question example there is `async function someWrapper()` but that function don't return anything (it even doesn't have `return` statement (!) ) - can you explain what do you mean by `async functions immediately return a promise` ? – Kamil Kiełczewski Feb 08 '18 at 06:28
  • 1
    someWrapper will immediately return a promise that resolves to undefined. The await only "waits" in the someWrapper function but the function calling someWrapper will immediately receive a promise that resolves in undefined. Functions always return something, if you don't in code then it will return undefined. If it's an async function without a return then it'll return a promise that resolves in undefined. – HMR Feb 08 '18 at 06:31
  • @Sidney - sorry but I see that your answer in comment did not fully explain the problem. HMR provide better answer so I accept it. – Kamil Kiełczewski Feb 08 '18 at 07:52
  • I was hoping that a short explanation would help. I didn't have time to write a full answer. I'm glad that HMR was able to explain! – Sidney Feb 08 '18 at 17:32

2 Answers2

3

Looking at the comments

@HMR - hm... I not understand - in question example there is async function someWrapper() but that function don't return anything (it even doesn't have return statement (!) ) - can you explain what do you mean by async functions immediately return a promise? - Kamil Kielczewski

it seems you don't understand the async await. I usually advice people to lay off await until you understand promises. However in next comment under question I give you the answer:

someWrapper will immediately return a promise that resolves to undefined. The await only "waits" in the someWrapper function but the function calling someWrapper will immediately receive a promise that resolves in undefined. Functions always return something, if you don't in code then it will return undefined. If it's an async function without a return then it'll return a promise that resolves in undefined - HMR.

Await is syntax sugar (nicer looking code) for promises and doesn't actually wait for anything.

Maybe the following code clears things up:

var test = async () => {
   await 22;//doesn't even matter if value is promise
   console.log("after wait");
}
var result = test();
console.log("outside test we don't wait for anything",result);

If you don't understand why the output of that code is:

outside test we don't wait for anything Promise {< pending >}

after wait

Then I'd advice you to use just promises, until you do.

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    I edit your answer and add our queestion-comments which provide key hint - that `async function` is not "normal function" but it always return promise (this is difference between async and normal (non-async) function - from which I did not realize) - this was key point to me to understand why question example behave like that – Kamil Kiełczewski Feb 08 '18 at 07:55
  • There is also one thing: you say that `Await is syntax sugar (nicer looking code) for promises and doesn't actually wait for anything.` - 'anything' **except** Promise "finish" (resolved or rejected) - ? – Kamil Kiełczewski Feb 08 '18 at 07:59
  • I also have question for your snippet - if I remove `await 22` line ,then first line in console will be `after wait` (sequence of console line print switch). And I don't get it - why in this case, promise from `var result = test();` is executed immediately (and print something to console)? – Kamil Kiełczewski Feb 08 '18 at 08:15
  • @KamilKiełczewski It doesn't wait for anything outside the the async function.When no await is in the function then no code after await is queued so the order of the logs are different. I'd advice again to use the promise syntax and not the async. Just updated [another answer](https://stackoverflow.com/a/48677470/1641941) (under update) that will probably give me some push back from OO people but I think a promise object is a perfect data type and async and await are usually only needed because you're writing a function that is doing too much. – HMR Feb 08 '18 at 08:25
  • Maybe [this](https://stackoverflow.com/a/47678417/1641941) can help too. – HMR Feb 08 '18 at 08:26
2

test2:

your test2 is not async until you make it async. You've wrote synchronous code inside test2.they are the console.log. Only async code inside test2 is call to promise.Let's break it down

async function test2() {
    for(let i=0; i<5; i++) {
        someWrapper(i);                
    }    
}

above code fires someWrapper() 5 times sequentially.so it write the first sync code which is console.log('A'+i) 5 times in a row in console.

then each someWrapper() waits for async promise to return parallelly.after each promise resolved it prints 'Inside promise'. until the promise resolves, the execution halts and can not proceed to next step

then, after resolving the promise, it prints out the second sync code which is console.log('B'+i) in console

test1:

test1 will behave differently than test2.Let's break it down

async function test1() {
    for(let i=0; i<5; i++) {
        // body copy-pasted of someWrapper function:
        console.log('A: '+ i);
        await getSomePromise();
        console.log('B: ' + i);
    }    
}

the main distinction is you are awaiting inside for loop. So this will literally pause the loop which was not the case for test1

so for each iteration it will print console.log('A'+i)

then pause the iteration for await getSomePromise()

when the promise return will print 'Inside Promise'

then print console.log('B'+i)

then continue next iteration.

AL-zami
  • 8,902
  • 15
  • 71
  • 130
  • @AL-zami - in Your opinion this JS behavioru is intuitive or counterintuitive? (because in my point of view, during refactor, I only move for-loop body into separate function and I was supprised that this move change behaviour...) – Kamil Kiełczewski Feb 07 '18 at 18:10
  • @KamilKiełczewski actually javascript is single threaded. inside async code , sync code wiill run in synchronous manner. async doesn't make sync code async. – AL-zami Feb 07 '18 at 18:12
  • one thing , which is ,this is not counterintuitive. both promise and async-await are introduced to remove the mess of callback chain. They help to write async code in sync manner – AL-zami Feb 07 '18 at 18:42
  • 1
    @AL-zami I think it's counter intuitive since the OP and many other questions I've seen think that `await` actually awaits instead of the `async` function immediately returning a promise. If people learn to work with promises before using async syntax there would have been a lot less questions about this here. – HMR Feb 08 '18 at 03:22
  • 1
    @HMR yeah! i also have faced difficulties understanding it in the first place. But when the picture becomes clear it doesn't seem so counter intuitive anymore.I agree with you , the learning curve should be Promise > Generators > async-await – AL-zami Feb 08 '18 at 04:27