2013-12-04 6 views
5

Рассмотрим следующий код:Почему javascript setTimeout() не работает в цикле?

<!DOCTYPE html> 
<html> 
<head> 
<script> 
function timedText() 
{ 
    var x=document.getElementById('txt'); 
    var t = new Array(); 
    t[1] = setTimeout(function(){x.value="2 seconds"}, 2000); 
    t[2] = setTimeout(function(){x.value="4 seconds"}, 4000); 
    t[3] = setTimeout(function(){x.value="6 seconds"}, 6000); 
} 
function timedTextArr() 
{ 
    var x=document.getElementById('txt'); 
    var t = new Array(); 
    for(var i = 0 ; i < 3 ; i++) { 
    t[i] = setTimeout(function(){x.value=i*2+" seconds"}, i*2000); 
    } 
} 
</script> 
</head> 
<body> 
<form> 
<input type="text" id="txt" /> 
<input type="button" value="Display timed text!" onclick="timedText()" /> 
<input type="button" value="Display timed text Arr!" onclick="timedTextArr()" /> 
</form> 
<p>Click on the button above. The input field will tell you when two, four, and six seconds have passed.</p> 
</body> 
</html> 

Функция timedText() работает, но timedTextArr() не делает. Обе функции присваивают значениям от setTimeout() элементам массива. Но в цикле for() работает только последний таймер ... и он работает три раза.

Это ошибка?

ответ

5

Это не ошибка, посмотрите, какие замыкания находятся в Javascript.

В основном в вашем цикле для функции только

function(){x.value=i*2+" seconds"} 

«видит» один экземпляр переменной я.

Итак, как только цикл закончен, i равно 3, поэтому он равен 3 для всех функций.

Вам нужно обернуть вызов в другой анонимной функции, чтобы создать область, например:

t[i] = setTimeout((function(i){ return function(){x.value=i*2+" seconds"}})(i), i*2000); 

Внешняя функция создает новую область, и внутри него я буду равен значению I в петле и оставаться таким образом. Вы можете попробовать: http://jsfiddle.net/6b68E/

+0

Интересно. Я буду исследовать «закрытие javascript». Спасибо. – user2624632

1

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

чтобы преодолеть это, вы должны рассмотреть вопрос рефакторинга кода на использование интервала вместо, который позволяет изменять значение i синхронно с закрытием:

var i=1; 

var handle = setInterval(function() { 

x.value = (i*2) + "seconds"; 
i++; 
if (i>3) clearInterval(handle); 

}, 2000); 

Кроме того, цикл работает от 0 до 2, а не от 1 до 3, как в timedText()

+0

Это нормально, поскольку я хотел, чтобы первый setTimeout() был 0 секунд, чтобы пользователь сразу увидел что-то. – user2624632

1

Причина, по которой вы получаете те же результаты: setTimeout является асинхронным. Это означает, что он не запускается до завершения оставшейся части скрипта. Затем, когда он работает значение i устанавливается равным 3, поэтому все функции запуска и все они видят, что я = 3.

3

i в функции относится к i из цикла, который 6 самая время любого из тайм-аутов. Вам нужно добавить закрытие/область действия:

for(var i = 0 ; i < 3 ; i++) { 
    (function(){ // create a closure (new scope) 
     var _i = i; // make a local copy of `i` from the outer scope 
     t[i] = setTimeout(function(){x.value=_i*2+" seconds"}, i*2000); 
    })(); 
    } 
Смежные вопросы