You're running into a few common pitfalls when dealing with asynchronous code.
The first thing to realize is that your for loop is not waiting for each iteration to complete. It's simply invoking timer.start(5)
four times, as quickly as it can. In order to do this, you can't use a for loop. Your best bet is to use something from the very popular async library instead.
The second thing to realize is that you're invoking start(5)
on the same instance repeatedly, causing it to reset to five seconds each time. As a result, only one of your scheduled callbacks is being invoked, five seconds after the loop has completed. This is really less a misunderstanding of async code, and more a misunderstanding of the timer.js
library itself. You can fix this by just creating new timers in the loops, but for your case timer.js
is a bit overkill. You can do exactly what you want with native setTimeout
calls.
The third thing to realize is that the callback you're sending to on()
does not store the current value of i
. It simply stores a reference to that variable, so any changes to that variable will ultimately be reflected in the log.
During the last iteration of the loop, the value of i
is actually 3, not 4. This is because 4 is not <
nums.length. So, the only callback to execute should log a 3, right?
Not so fast, though. The last thing the loop always does after each iteration is increment the value of i
. So, i
goes from 3 to 4, then checks if it's <
nums.length, and since it's not, it exits. Then, five seconds later, your callback logs the value of i
, which is now 4.
This is a property of something called a closure in Javascript. I definitely recommend reading that link, because the concept is pretty crucial for using the language effectively.
Anyways, since you're probably looking for correct code here's how I would do what you're attempting, using async
and setTimeout
:
var nums = [1,2,3,4];
async.eachSeries(nums, (num, done) => {
setTimeout(() => {
console.log(num);
done();
}, 5000);
});