setInterval (javaScript):是否有已知的错误?

发布于 2024-12-05 14:17:10 字数 277 浏览 2 评论 0原文

100 次中有 99 次这是完美的:

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

有时第一个循环会等待 20 秒到 2 分钟。此后就完美运行了。我知道计时器可以在 Android 手机上暂停(当显示软键盘时)。是否还有其他条件可能会延迟 setInterval?

99 times out of 100, this works perfectly:

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

Occasionally the first loop waits for 20 seconds to 2 minutes. Thereafter it runs perfectly. I know the timer can pause on Android phones (when the soft keyboard is shown). Are there other conditions that might delay setInterval?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

腻橙味 2024-12-12 14:17:10

首先,强烈建议您提供回调(function)作为第一个参数而不是字符串,因为该字符串是在全局范围内计算的,我们都知道当我们使用 < 时会发生不好的事情。 js 中的 code>eval (相关 eval 帖子:什么时候是 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(几乎)无限次,但永远不会完成。

这种情况的解决方案是用 setTimeout 模拟 setInterval,以确保回调在再次调用之前已完成执行:

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);
}

如果您感兴趣,我重写了 setIntervalclearInterval 函数,以便在任何地方使用它们,而无需处理堆栈溢出:

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;
} 

Firstly, it is strongly advised you provide a callback(function) as the first argument and not a string, because that string is evaluated in the global scope and we all know that bad things happen when we use eval in js (related eval post : When is JavaScript's eval() not evil?).
So, your

setInterval("b()", 1000); 

should be rewritten as :

setInterval(b, 1000); 

or:

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

I also recommend you use setTimeout to simulate a setInterval.

The main downfall of the setInterval function is that it executes a block of code every n milliseconds, regardless of the execution of the previous block of code.
So if for some reason a setInterval callback takes longer to execute than the delay provided, it will cause some stack overflows.

Let's take the following code for example :

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

This will actually freeze the browser because it will execute foo for an (almost) infinite number of times but it will never finish it.

The solution in this kind of case is to emulate the setInterval with setTimeout, in order to ensure that the callback has finished to execute before calling it again:

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);

Now, foo is called again only after the previous instance has finished the code execution.

I would apply the same thing to your code, in order to let the browser decide when it can execute the code, and not to force it to execute the block of code weather it's busy at that moment or not:

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);
}

If you're interested, I've rewritten the setInterval and clearInterval functions in order to use them anywhere, without taking care of stack overflows :

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;
} 
权谋诡计 2024-12-12 14:17:10

试试这个,

setInterval(b, 1000);

或者

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

try this,

setInterval(b, 1000);

or

setInterval(function(){
   timer++;
   updateText(timer);
}, 1000);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文