04 сентября 2014

setTimeout в цикле

Задача по javascript.
Что будет выведено в консоли? Почему?
for(var i=1; i<=2; i++){
    setTimeout(function() { console.log(i) }, 100);
}


Решение:
Запустив, скрипт, вы увидите, что будет выведено дважды число 3, а не 1, 2, как можно предположить.
Это связано с тем, что при таком написании кода все создаваемые таймеры используют одну и ту же переменную i. Чтобы для каждого таймера предоставить свою переменную i, код следует переписать:

1 способ:
function makeSetTimeout(i) {
    setTimeout(function() { console.log(i); }, 100);
}

for (var i = 1; i <= 2; ++i) {
  makeSetTimeout(i);
}
//В консоли: 1 2

В этом случае переменная i передается в функцию, в которой и происходит создание таймера. Следовательно при срабатывании таймер использует переменную i, которую мы передали в функцию, а те ну, что определена глобально.

2 способ:
for(var i = 1; i <= 2; i++) {
    (function(index) {
        setTimeout(function() { console.log(index); }, 100);
    })(i);
}
//В консоли: 1 2

Здесь на каждой итерации цикла мы передаем в анонимную функцию параметр i. В этой же анонимной функции происходит создание таймера, который будет использовать переменную, переданную в эту функцию.

Внимание: в приведенных примерах числа 1 и 2 будут выведены почти одновременно, без задержки. Это связано с тем, что таймеры имеют одну и ту же задержку.
А что если нам нужно сначала вывести цифру 1, а через 3 секунды цифру 2. Для этого нужно увеличить задержку у вновь создаваемого таймера:
for(var i = 1; i <= 2; i++) {
    (function(index) {
        setTimeout(function() { console.log(index); }, 3000*i);
    })(i);
}


1 комментарий: