2015-05-25 6 views
1

Я читал из нескольких мест, которые setTimeout() предпочтительнее для setInterval(), когда вы устанавливаете что-то, чтобы в основном работать вечно. Код ниже работает нормально, но примерно через час работы Firefox (38.0.1) выдает ошибку too much recursion.jquery setTimeout слишком много рекурсии

По существу, у меня он захватывает очень небольшое количество текста из counts.php и обновляет таблицу с помощью этой информации. По словам инспектора, весь призыв и возвращение занимает около 50 мс. Я пытаюсь сделать это каждые шесть секунд по указанию t.

Я подозреваю, что если я переключусь на setInterval(), это, вероятно, сработает, но я не был уверен, что текущее состояние метода setTimeout() и setInterval(), как и все, что я нашел, 5 лет.

$(document).ready(function() { 
    t = 3000; 
    $.ajaxSetup({cache: false}); 

    function countsTimer(t) { 
     setTimeout(function() { 
      $.getJSON("counts.php", function (r) { 
       $(".count").each(function(i,v) { 
        if ($(this).html() != r[i]) { 
         $(this).fadeOut(function() { 
          $(this) 
           .css("color", ($(this).html() < r[i]) ? "green" : "red") 
           .html(r[i]) 
           .fadeIn() 
           .animate({color: '#585858'}, 10000); 
         }) 
        }; 
       }); 

       t = $(".selected").html().slice(0,-1) * ($(".selected").html().slice(-1) == "s" ? 1000 : 60000); 

       countsTimer(t); 
      }); 
     }, t); 
    }; 
    countsTimer(t); 
}); 

Update: Эта проблема была решена путем добавления .stop (правда, истина) до .fadeOut() анимации. Эта проблема произошла только в Firefox, поскольку тестирование в других браузерах не вызвало никаких проблем. Я правильно ответил на этот вопрос, несмотря на то, что это не решение в данном конкретном случае, а скорее дает хорошее объяснение в более общем смысле.

+1

Я не знаю, почему 'setInterval' не следует использовать. Каковы были причины? Похоже на ложные претензии или причины, которые имеют отношение только к конкретным ситуациям. –

+0

Мне интересно, если очистка таймаута устранит эту проблему, даже если я действительно не уверен, откуда происходит рекурсия –

+0

@squint, чтобы избежать какой-либо проблемы, если запрос делает больше, чем задержка, чтобы завершить –

ответ

1

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

Например, если у вас есть функция, выполняющая каждые 1 с использованием setInterval, однако сама функция забирает 2s из-за медленного запроса XHR, эта функция будет работать в то же время в то же время в какой-то момент. Это часто нежелательно. Используя setTimout и вызывая, что в конце исходной функции функция никогда не перекрывается, и установленный вами тайм-аут всегда является временем между двумя вызовами функций.

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

(function() 
{ 
    var lastCheck = Date.now(), alreadyRunning = false; 
    document.addEventListener 
    (
     "click", 
     function() 
     { 
      if(!alreadyRunning && Date.now() - lastCheck > 3000) 
      { 
       alreadyRunning = true; 
       /* Do your request here! */ 
       //Code below should run after your request has finished 
       lastCheck = Date.now(); 
       alreadyRunning = false; 
      } 
     } 
    ) 
}()); 

Это не недостаток setInterval делает, потому что вы всегда проверить, если код уже запущен, однако проверка выполняется только при получении события браузера. (Обычно это не проблема.) И этот метод вызывает гораздо более шаблонный.

Итак, если вы уверены, что запрос XHR не займет больше 3 секунд, просто используйте setInterval().

Edit: Ответ выше неправильно в некоторых аспектах

Как уже указывалось в комментариях, SetTimeout() действительно не увеличить размер стека вызовов, так как он возвращает до функции в тайм-аут называется. Также функция в вопросе не содержит никакой конкретной рекурсии. Я сохраню этот ответ, потому что часть вопроса касается setTimeout() vs setInterval(). Однако проблема, вызвавшая ошибку рекурсии, вероятно, будет в каком-то другом фрагменте кода, поскольку нет функции, вызывающей себя, прямо или косвенно, в любом месте примерного кода.

+0

Я должен был добавить, что использование страницы в том, что она будет отображаться и никогда не взаимодействовать с ним. Таким образом, он мог бы сидеть там часами без мыши, когда-либо двигавшейся. причина для того, чтобы не использовать setInterval только вероятность того, что он может перекрываться и уже запускать экземпляр? Как вы использовали здесь, добавив чек для 'alreadyRunning', в значительной степени облегчите эту проблему? – mbazdell

+0

В этом случае это пример учебника правильного использования для setInterval(). – w3re

+0

Независимо от вопроса, следует ли использовать 'setTimeout' или' setInverval', ответ неверен в части увеличения стека вызовов функции. 'countsTimer' возвращает и удаляет себя из стека до того, как функция в' setTimeout' выполняется со следующим вызовом 'countsTimer'. По крайней мере, это должно быть. – nomve

Смежные вопросы