Есть setTimeout внутри цикла for:
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
Я хочу показывать числа 1, 2, 3, 4, 5, но показывает 6, 6, 6, 6, 6. Почему?
Есть setTimeout внутри цикла for:
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
Я хочу показывать числа 1, 2, 3, 4, 5, но показывает 6, 6, 6, 6, 6. Почему?
Дело в том, что функция выполняется после того, что цикл закончится. Поэтому, i уже равно 6, когда console.log(i) выполняется первый раз.
Если еще непонятно, вот похожий пример в псевдокоде:
У меня 1 камень.
Через минуту скажи, сколько у меня камней.
Дай мне камень сейчас.
Через 2 минуты скажи, сколько у меня камней.
Дай мне камень сейчас.
Получится, что сейчас мне даст 2 камня, в итоге у меня будут 3. Через минуту скажет, сколько у меня камней (т.е. 3), и через две минуты опять скажет, что у меня 3 камня.
i функции вывода на каждой итерации. Это самое обычное решение. for(var i = 1; i <= 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
Создавать много одинаковых функций - не очень хорошо, можно поднять эту функцию выше:
function pass(i) {
return function () {
console.log(i);
}
}
for(var i = 1; i <= 5; i++) {
setTimeout(pass(i), i * 1000);
}
(function f(i) {
if (i > 5) return;
setTimeout(function() {
console.log(i);
f(i + 1);
}, 1000);
})(1);
Function.prototype.bind(), чтобы создать новую функцию на каждой итерации. Это короче первого варианта, но IE8 и ниже не поддерживают .bind.for (var i = 1; i <= 5; i++) {
setTimeout(function(i) {
console.log(i);
}.bind(null, i), i * 1000);
}
setTimeout. Это поддерживают все современные браузеры, но если речь идёт о старых, то стоит проверить.for(var i = 1; i <= 5; i++) {
setTimeout(function (i) {
console.log(i);
}, i * 1000, i);
}
Замечу, что теперь функции в цикле ничем не отличаются друг от друга, поэтому можно сделать одну функцию:
function doSmth(i) {
console.log(i);
}
for(var i = 1; i <= 5; i++) {
setTimeout(doSmth, i * 1000, i);
}
Использовать let . Это удобный вариант, но это новая возможность в ECMAScript 2015, так что еще не работает в большинстве браузеров. Если хотите использовать ECMAScript 2015 до того, что браузеры его поддерживают, рекомендую попробовать Babel.
Внимание: некоторые браузеры (например, IE 11) поддерживают let, но не поддерживают его в цикле for.
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
i + 1 в setTimeout. Могли бы объяснить? И да, в этом случае вариант setInterval удобен, но я хотел ответ, который работает в общем случае. Может быть, вместо setTimeout, там запрос AJAX или другая асинхронная функция.
– Peter Olson
Jul 06 '15 at 07:19
setInterval будет работать ровно так же.
– Regent
Jul 06 '15 at 07:29
2 * n функций vs n + 1.В ответе можно написать как-нибудь в стиле "Создавать 2*n (n внутренних и n внешних) функций для setTimeout - не очень хорошо, можно вынести создание внутренней функции за цикл, сократив тем самым количество функций до n+1"
– Regent
Oct 18 '18 at 09:41
iв цикле и i в function(i) одинаковая или можно переименовать ?
– Никита Фаст
Oct 19 '18 at 15:35
i. "можно переименовать?" - я бы даже сказал "нужно", чтобы не путаться
– Regent
Oct 19 '18 at 16:33
Решение на async
const intArray = [1, 2, 3, 4, 5];
async.eachSeries(intArray, function(i, callback) {
setTimeout(function() {
console.log(i);
callback();
}, i * 1000);
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if( err ) {
console.log('An error occured. All processing has stopped');
} else {
console.log('Whole array processed successfully');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/2.4.0/async.js"></script>