scarably scarably - 1 year ago 56
Node.js Question

Async code execution explanation

I have been trying to wrap my head around the issue I have but without success therefore I am asking for your help.

Let's consider following example:

const async = require('async')
var counter = 0

var test = cb => {
if (++counter < 3) {
setTimeout( () => {
async.each([0,1,2], (item, callback) => {
console.log(item)
console.log('counter inner ' + counter)
test(cb)
callback()
}, () => {
cb()
})
})
} else {
console.log('counter ' + counter)
}
}

var prom = new Promise( (res, rej) => test( () => res('done') ) )
prom.then(res => console.log(res))


The output is:

0
counter inner 1
1
counter inner 2
counter 3
2
counter inner 3
counter 4
done
0
counter inner 4
counter 5
1
counter inner 5
counter 6
2
counter inner 6
counter 7


What I do not understand is how it's printed
counter inner 4
right after the second
0
. Shouldn't be printed
counter inner 1
? And a promise is being resolved only once? What happens with the second resolve call? Thank you.

Answer Source

Kind of difficult to explain it step by step, but I will try backwards - meaning from your output I will try to mention which line in your code outputed it and when.

// obviously starting inside the callback async runs for each of your 3 items

    0 // console.log(item);
    counter inner 1 // console.log('counter inner ' + counter);

    1 // console.log(item);
    counter inner 2 // console.log('counter inner ' + counter);
    counter 3 // let's not forget you also call callback() for each item 
             // which evaluates this condition if (++counter < 3) and starting from here,
             // it will be true and will run your else statement
             // console.log('counter ' + counter);

    2 // console.log(item);
    counter inner 3 // console.log('counter inner ' + counter);
    counter 4 // console.log('counter ' + counter);

// at this point your promise is already resolved (and yes, only once)
    done // prom.then(res => console.log(res))

// Now you are probably questioning where the following output comes from
// remember this condition? if (++counter < 3)
// before starting to evaluate to false and printing to console, once it
// evaluated to true so setTimeout was called with your async callback
// which scheduled it to run on the next "tick"*, which is right after the
// promised was resolved

    0
    counter inner 4
    counter 5 
    1 
    counter inner 5
    counter 6
    2
    counter inner 6
    counter 7

This is the effect that setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually re-queues the new javaScript code at the end of the execution queue.

According to setTimeout documentation, its second parameter is the following:

The time, in milliseconds (thousandths of a second), the timer should wait before the specified function or code is executed. If this parameter is omitted, a value of 0 is used, meaning execute "immediately", or more accurately, as soon as possible. Note that in either case, the actual delay may be longer than intended;

And here you can read more about reasons for delays longer than specified.

You can easily verify what I just explained by changing your if condition to if(++counter < 2). Since it will never evaluate to true, you will see that the console ouput stops at "done".

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download