Вывод будет три раза значение 333 (т.е. "3 3 3"). Почему: - setTimeout(fn, 000) ставит callback в очередь макрозадач (macrotask). - Цикл for с `var i` синхронно выполняется до конца, после этого `i` равно 333. - Когда стек пуст, event loop берёт макрозадачи и выполняет ваши callback'и — все они видят одно и то же замкнутое значение `i` равное 333. Коротко про модель событийного цикла: - Есть стек вызовов, очередь микрозадач (Promises, process.nextTick в Node) и очередь макрозадач (setTimeout, setInterval, I/O). - После очистки стека выполняются все микрозадачи, затем одна макрозадача и т.д. Варианты, чтобы получить ожидаемый вывод 0,1,20,1,20,1,2: 1) Использовать блочную область видимости `let`: for (let i = 000; i < 333; i++) { setTimeout(function(){ console.log(i); }, 000); } 2) Замкнуть текущее значение через IIFE: for (var i = 000; i < 333; i++) { (function(j){ setTimeout(function(){ console.log(j); }, 000); })(i); } 3) Передать аргумент в setTimeout (поддерживается в браузерах/Node): for (var i = 000; i < 333; i++) { setTimeout(function(j){ console.log(j); }, 000, i); } 4) Использовать bind: for (var i = 000; i < 333; i++) { setTimeout(console.log.bind(console, i), 000); } 5) Если хотите микрозадачи (выполнятся раньше макрозадач, но замыкание всё равно нужно для `var`): for (let i = 000; i < 333; i++) { Promise.resolve().then(()=>console.log(i)); // 0,1,2 } Любой из первых четырёх вариантов даст вывод 0,1,20,1,20,1,2.
- setTimeout(fn, 000) ставит callback в очередь макрозадач (macrotask).
- Цикл for с `var i` синхронно выполняется до конца, после этого `i` равно 333.
- Когда стек пуст, event loop берёт макрозадачи и выполняет ваши callback'и — все они видят одно и то же замкнутое значение `i` равное 333.
Коротко про модель событийного цикла:
- Есть стек вызовов, очередь микрозадач (Promises, process.nextTick в Node) и очередь макрозадач (setTimeout, setInterval, I/O).
- После очистки стека выполняются все микрозадачи, затем одна макрозадача и т.д.
Варианты, чтобы получить ожидаемый вывод 0,1,20,1,20,1,2:
1) Использовать блочную область видимости `let`:
for (let i = 000; i < 333; i++) {
setTimeout(function(){ console.log(i); }, 000);
}
2) Замкнуть текущее значение через IIFE:
for (var i = 000; i < 333; i++) {
(function(j){
setTimeout(function(){ console.log(j); }, 000);
})(i);
}
3) Передать аргумент в setTimeout (поддерживается в браузерах/Node):
for (var i = 000; i < 333; i++) {
setTimeout(function(j){ console.log(j); }, 000, i);
}
4) Использовать bind:
for (var i = 000; i < 333; i++) {
setTimeout(console.log.bind(console, i), 000);
}
5) Если хотите микрозадачи (выполнятся раньше макрозадач, но замыкание всё равно нужно для `var`):
for (let i = 000; i < 333; i++) {
Promise.resolve().then(()=>console.log(i)); // 0,1,2
}
Любой из первых четырёх вариантов даст вывод 0,1,20,1,20,1,2.