JavaScript, как использовать setTimeout на итеративной операции со списком? - PullRequest
0 голосов
/ 13 ноября 2011

Я хочу сделать что-то вроде этого:

for(var i=0;i<aList.length;i++)
{
    aList[i].doSomething();
    sleep(500);
}

Конечно, в javascript нет функции сна, поэтому я попробовал следующее:

for(var i=0;i<aList.length;i++)
{
    setTimeout(function(){
        aList[i].doSomething();
    },500);  
}

Однако теперь он говорит aList[я] не определено.Поскольку анонимная функция является замыканием, она фактически читает aList [i] из области действия внешней функции, и, таким образом, к моменту запуска функции в setTimeout i уже изменилось.

Как это сделать?

Ответы [ 5 ]

6 голосов
/ 13 ноября 2011

Быстрое исправление для эмуляции JavaScript 1.7 let - это обернуть его в функцию:

for(var i=0; i < aList.length; i++) {
    (function(i) {
        setTimeout(function() {
            aList[i].doSomething();
        }, 500 * i); // <-- You need to multiply by i here.
    })(i);
}

Я также добавил исправление в небольшую ошибку, из-за которой скрипт останавливался на 500 секунд,затем выполните все из них.setTimeout неблокирует.

4 голосов
/ 13 ноября 2011

Это то, что вы, вероятно, хотите:

for(var i=0;i<aList.length;i++)
{
    (function(i){
        // Retain `i` in this scope, for use later (after timeout)
        setTimeout(function(){
           aList[i].doSomething();
        }, 500 * i);
    })(i);
}

Вы хотите 500 * i, чтобы каждый шаг был на 500 мс позже, чем последний, иначе все произойдет сразу после 500 мс. Обратите внимание на дополнительную функцию обертывания - она ​​по существу захватывает / сохраняет значение i.

1 голос
/ 13 ноября 2011

Один из вариантов:

var array = [/*elements*/],
    i,
    length = array.length,
    loop = setInterval(function() {
        array[i].doSomething();
        i += 1;
        if (i === length) {
            clearInterval(loop);
        }
    }, 500);
1 голос
/ 13 ноября 2011

Просто определите функцию до истечения времени ожидания

for(var i=0; nextFunction = aList[i];i++) {
    var waitAndDoSomething = function(func) {
        return function() { func.doSomething(); }
    };
    setTimeout(waitAndDoSomething(nextFunction),500*i);  
}

Другой возможный ответ

По предложению minitect я начал думать о других возможных решениях.Решение, которое мне кажется чистым, заключается в использовании bind

String.prototype.doSomething = function() { alert(this); } // Just for testing
var aList = ['one', 'two', 'three']; // Just for testing

for(var i=0;obj = aList[i];i++) { 
    setTimeout(obj.doSomething.bind(obj),500*i); 
}

Однако я не уверен, насколько хорошо это будет работать в контексте вопроса Razor Storm, потому что я не знаю, что такое aList [я] представлял бы.

Отвечает ли это на вопрос

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

String.prototype.doSomething = function() { alert(this); }
var aList = ['one', 'two', 'three'];

var doNextSomething = function(index) {
    if (!index) { index = 0 }
    if (nextObject = aList[index]) {
        nextObject.doSomething();
        setTimeout(doNextSomething, 500, index + 1);    
    }
};

doNextSomething();

Теперь она будет ждать 500 миллисекунд после выполнения функции (очевидно, в длительном задании).

Я думаю, что опубликованные ответы былиболее того, о чем думал Razor Storm.

0 голосов
/ 13 ноября 2011

перед циклом for сделайте что-то вроде:

var _aList = aList;

В цикле вы должны установить время и задержку 500 * i.

Таким образом, результат будет:

var _aList = aList;
for(var i=0;i<aList.length;i++)
{
    (function(obj, index) {
        setTimeout(function(){
           obj.doSomething();
        },500 * index); 
    })(aList[i], i);
}
...