Переполнение стека Javascript при запуске метода setTimeout + передача переменной - PullRequest
0 голосов
/ 16 января 2012

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

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

вот моя функция:

function setblink(id) {
   var elm = document.getElementById(id);
    if (elm.color == "red"){
        elm.color = "black";
    }
    else{
        elm.color = "red";
    }
    setTimeout(setblink(id),500);
}

У меня есть массив идентификаторов для элементов, которые должны мигать, под названием repsToBlink.

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

for(var x in repsToBlink){
setTimeout(setblink(repsToBlink[x]),500);
}

Как я могу заставить этот код делать то же самое, не вызывая переполнение стека?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 16 января 2012

Вам нужно изменить setTimeout с

setTimeout(setblink(id),500);

до:

setTimeout(function() { setblink(id) },500);

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

2 голосов
/ 16 января 2012

setblink(id) вызывает функцию немедленно. Переполнение стека является признаком немедленного, а не отложенного выполнения, поскольку setTimeout планирует выполнение на более позднее время, поэтому будущий вызов не будет перенесен в текущий стек вызовов.

Поскольку setblink принимает аргумент, оберните его в аннулированную анонимную функцию для отложенной оценки .

function setblink(id) {
   var elm = document.getElementById(id);
    if (elm.color == "red"){
        elm.color = "black";
    }
    else{
        elm.color = "red";
    }
    setTimeout(function () {setblink(id)},500);
}

for (var x in repsToBlink){
    (function (id) {
        setTimeout(function () {setblink(id)},500);
    })(repsToBlink[x]);
}

Код требует дополнительных улучшений.

Если repsToBlink является массивом, вы должны циклически обрабатывать целочисленные индексы из repsToBlink (for (...;...;...)), а не свойства (for ... in). Однако если вместо этого вы будете использовать объект с идентификаторами и индексами (а не значениями), for ... in будет уместным.

Выше приведено срабатывание отдельного таймера для каждого id (что может перегружать браузер). Перемещая цикл в функцию, которая становится единственной функцией, подлежащей планированию, требуется только один таймер.

Поскольку вы периодически запускаете функцию, setInterval является более подходящим.

Всякий раз, когда вы удаляете и идентифицируете из repsToBlink, проверьте, есть ли еще какие-либо; если нет, отмените интервал.

(function () {
    var repsToBlink, repCount=0, blinkInterval;

    function startBlinking(ids) {
        addRepsToBlink(ids);
        if (! blinkInterval) {
            blinkInterval = setTimeout(blinkAll, 500);
        }
    }

    function addRepsToBlink(ids) {
        for (var i=0; i<ids.length; ++i) {
            addRep(ids[i]);
        }
    }

    function addRep(id) {
        if (! id in repsToBlink) {
            ++repCount;
            repsToBlink[ids[i]] = true;
        }
    }

    function removeRep(id) {
        if (id in repsToBlink) {
            delete repsToBlink[id];
            --repCount;
            if (!repCount) {
                clearInterval(blinkInterval);
                blinkInterval=0;
            }
        }
    }

    function blinkAll() {
        for (id in repsToBlink) {
            blink(id);
        }
    }

    function blink(id) {
        var elm = document.getElementById(id);
        if (elm.color == "red"){
            elm.color = "black";
        } else {
            elm.color = "red";
        }
    }

    window.startBlinking = startBlinking;
    window.addRepsToBlink = addRepsToBlink;
    window.addRep = addRep;
    window.removeRep = removeRep;
})();
1 голос
/ 16 января 2012

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

Интерпретатор, когда доходит до этого кода:

setTimeout(setblink(id),500);

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

Чтобы исправить это, оберните функцию, которую setTimeout должен вызывать внутри function(){}.

...