setInterval (javaScript): есть известные ошибки? - PullRequest
2 голосов
/ 21 сентября 2011

99 раз из 100, это прекрасно работает:

function a(){
   setInterval("b()",1000); 
   updateText("still working");
}
function b(){
   timer++;
   updateText(timer);
}

Иногда первый цикл ждет от 20 секунд до 2 минут.После этого он работает отлично.Я знаю, что таймер может останавливаться на телефонах Android (когда отображается программная клавиатура).Существуют ли другие условия, которые могут задержать setInterval?

Ответы [ 2 ]

10 голосов
/ 21 сентября 2011

Во-первых, настоятельно рекомендуется, чтобы вы указали обратный вызов (function) в качестве первого аргумента, а не строку, потому что эта строка оценивается в глобальной области видимости, и мы все знаем, что плохие вещи случаются, когда мы используем eval в js (связанный eval post: Когда JavaScript eval () не злой? ).
Итак, ваш

setInterval("b()", 1000); 

следует переписать как:

setInterval(b, 1000); 

или:

setInterval(function() { b(); }, 1000); 

Я также рекомендую использовать setTimeout для имитации setInterval.

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

Для примера возьмем следующий код:

function foo() {
    // this takes about 2 seconds to execute
    // .. code here
}
setInterval(foo, 1000);

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

Решение в этом случае состоит в том, чтобы эмулировать setInterval с setTimeout, чтобы гарантировать, что обратный вызов завершился, прежде чем вызывать его снова:

function foo() {
    // this takes about 2 seconds to execute
    // .. code here
}
function newSetInterval(callback, duration, callbackArguments) {
    callback.apply(this, callbackArguments);
    var args = arguments,
        scope = this;

    setTimeout(function() {
        newSetInterval.apply(scope, args);
    }, duration);
}
newSetInterval(foo, 1000);

Теперь foo вызывается снова только после того, как предыдущий экземпляр завершил выполнение кода.

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

function a() {
   newSetInterval(b, 1000); 
   updateText("still working");
}
function b() {
   timer++;
   updateText(timer);
}
function newSetInterval(callback, duration, callbackArguments) {
    callback.apply(this, callbackArguments);
    var args = arguments,
        scope=this;

    setTimeout(function() {
        newSetInterval.apply(scope, args);
    }, duration);
}

Если вам интересно, я переписал функции setInterval и clearInterval, чтобы использовать их где угодно, не заботясь о переполнениях стека:

function setInterval(f, time) {
    setInterval.ids = setInterval.ids || {};
    setInterval.idCount = setInterval.idCount || 0;
    var that = this,
        id = setInterval.idCount++,
        // to prevent firefox bug that adds an extra element to the arguments
        l = arguments.length - 2;

    (function theFn() {
        // to prevent firefox bug that adds an extra element to the arguments
        var args = [].slice.call(arguments, 0, l);
        f.apply(this, args);
        setInterval.ids[id] = setTimeout.apply(this, [theFn, time].concat(args));
    }).apply(that, [].slice.call(arguments, 2, arguments.length));
    return id;
}


function clearInterval(id) {
    if(!setInterval.ids || !setInterval.ids[id]) {
        return false;
    }
    clearTimeout(setInterval.ids[id]);
    return true;
} 
1 голос
/ 21 сентября 2011

попробуйте это,

setInterval(b, 1000);

или

setInterval(function(){
   timer++;
   updateText(timer);
}, 1000);
...