That’s kind of what I was trying to trying to tell you before… ;-)
It’s not a “bug in JS” though, it’s a bug in your code. :-O
Consider this simple loop:
for (let i = 0; i < 5; i++) {
window.setTimeout(console.log, 1000, 'Iteration no.', i)
}
You could write that out like this:
window.setTimeout(console.log, 1000, 'Iteration no.', 0)
window.setTimeout(console.log, 1000, 'Iteration no.', 1)
window.setTimeout(console.log, 1000, 'Iteration no.', 2)
window.setTimeout(console.log, 1000, 'Iteration no.', 3)
window.setTimeout(console.log, 1000, 'Iteration no.', 4)
This will log 5 lines to the console right away after 1s. This is indeed the desired behaviour of setting a timeout; it schedules the execution of a function (console.log
here) to a point in the future which is 1s from now. It does not “wait” or “sleep” if that’s what you expected; it does not block the thread, the following code keeps executing. This is actually pretty important. Anyway, compare this to
for (let i = 0; i < 5; i++) {
window.setTimeout(console.log, 1000 * i, 'Iteration no.', i)
}
which you could write out like
window.setTimeout(console.log, 0, 'Iteration no.', 0)
window.setTimeout(console.log, 1000, 'Iteration no.', 1)
window.setTimeout(console.log, 2000, 'Iteration no.', 2)
window.setTimeout(console.log, 3000, 'Iteration no.', 3)
window.setTimeout(console.log, 4000, 'Iteration no.', 4)
This will log the first line immediately, the 2nd after 1s, the 3rd after 2s etc. Another option is using an interval as shown by @PaulOB & @Paul_Wilkins. Or you can use recursion, if you prefer:
function delayLogging(i = 0) {
console.log('Iteration no.', i)
if (i < 5) {
window.setTimeout(delayLogging, 1000, i + 1)
}
}
delayLogging()
You could also use a generator to simulate a “sleep” behaviour, which actually does pause the code execution within the for
loop (still without blocking the thread, mind you):
const delay = iterable => {
const next = iterable.next()
if (!next.done) {
window.setTimeout(delay, 1000, iterable)
}
}
const doLogging = function * () {
for (let i = 0; i < 5; i++) {
yield console.log(i)
}
}
delay(doLogging())
Yet another possibility is to await
a promise to resolve the timeout:
const delay = () => new Promise(resolve => {
window.setTimeout(resolve, 1000)
})
async function doLogging () {
for (let i = 0; i < 5; i++) {
console.log('Iteration no.', i)
await delay()
}
}
doLogging()
You see there are many ways to solve the problem, but the language is fine. :-)