Проблемы с областью и временем для циклов for и setTimeout. - PullRequest
1 голос
/ 28 августа 2011

Это должно быть легким, но моя голова, кажется, измотана только сейчас.

Результат, к которому я стремлюсь, составляет 0,1,2,0,1,2

Мой код пока ... здесь - это скрипка для игры.

function delayedLoad(page){    

    console.log(page);

};

function loadContent(){
    var i,
        len = 3   

    for (i = 0; i < len; i++) {

        console.log(i);

        setTimeout(function(){
            delayedLoad(i);
        },3000*i);   

    }
};

$(document).ready(function(){
    // Load the content for the first time.
    loadContent();
}); 

Ответы [ 2 ]

3 голосов
/ 28 августа 2011

Это очень распространенная проблема, с которой сталкиваются люди.

Проблема в том, что функция, которую вы передаете setTimeout в каждой итерации, ссылается на такую ​​же i переменную.

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

Пример: http://jsfiddle.net/GNwhR/3/

function set_up_timeout( j ) {
    setTimeout(function(){
        delayedLoad( j );
    },3000*j);
}


function loadContent(){
    var i,
        len = 3   

    for (i = 0; i < len; i++) {

        console.log(i);

        set_up_timeout( i ); 

    }
};

Это помещает ваш setTimeout вызов в другую функцию, которая вызывается и передается i в каждой итерации. Это создает новую область видимости переменной, где j в функции ссылается на правильное значение.


Другой подход заключается в том, чтобы вызвать вызываемую функцию return a функцию:

Пример: http://jsfiddle.net/GNwhR/4/

function set_up_callback( j ) {
    return function(){
        delayedLoad( j );
    };
}


function loadContent(){
    var i,
        len = 3   

    for (i = 0; i < len; i++) {

        console.log(i);

        setTimeout( set_up_callback( i ), 3000*i ); 

    }
};

Этот вызывает set_up_callback, что возвращает функцию, которая ссылается на j.


Наконец, еще один распространенный подход - использовать IIFE (немедленно вызванное выражение функции) для устранения названной функции. Это менее понятно, ИМО:

Пример: http://jsfiddle.net/GNwhR/5/

function loadContent(){
    var i,
        len = 3   

    for (i = 0; i < len; i++) {

        console.log(i);

        setTimeout((function( j ){
            return function() {
                delayedLoad( j );
            };
        })( i ),3000*i);   

    }
};

Это в основном то же самое, что и предыдущий пример, в котором он возвращает функцию. Основное отличие состоит в том, что функция, которая возвращает функцию, создается и вызывается внутри самого цикла.

Все это иллюстрирует ту же концепцию, что вам нужно охватить переменную, если вы хотите позднее асинхронно ссылаться на нее и сохранить ее ожидаемое значение.

1 голос
/ 28 августа 2011

объяснение Патрика довольно хорошо.Вот небольшой код, который поможет вам обойти это.

function delayedLoad(page){    

    console.log(page);

};

function loadContent(){
    var i,
        len = 3    

    for (i = 0; i < len; i++) {

        console.log('in loop', i);

        setTimeout($.proxy(function(){
            delayedLoad(this.page);
        }, {page: i}),3000*i);   

    }
};

$(document).ready(function(){
    // Load the content for the first time.
    loadContent();
});

Прокси-сервер jQuery просто сбрасывает то, на что указывает «this» в функции ... так что вы можете заменить передачу числа простым сбросомсфера :) (если это имеет смысл .. длинный день)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...