jquery setTimeout 太多递归

jquery setTimeout too much recursion

我从多个地方了解到,在将某些内容设置为永远 运行 时,setTimeout() 比 setInterval() 更可取。下面的代码工作正常,但在 运行ning Firefox (38.0.1) 大约一个小时后抛出 too much recursion.

错误

基本上,我让它从 counts.php 中抓取了非常少量的文本,并用该信息更新了 table。根据检查员的说法,整个调用和 return 大约需要 50 毫秒。我试图让它按照 t.

的指示每 x 秒执行一次

我怀疑如果我切换到 setInterval() 这可能会起作用,但我不确定 setTimeout() 与 setInterval() 心态的当前状态是什么,因为我发现的一切都是关于 3 -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);
});

更新: 通过在 .fadeOut() 动画之前添加 .stop(true, true) 解决了这个问题。此问题仅发生在 Firefox 中,因为在其他浏览器中进行测试未导致任何问题。我已将答案标记为正确,尽管它不是这种特定情况下的解决方案,而是从更一般的意义上提供了一个很好的解释。

在这种情况下,您确实应该切换到 setInterval()。 setInterval() 的问题是,如果您想要清除超时,您要么必须保留一个引用,并且如果操作(可能)执行的时间比超时本身长,则操作可能是 运行 两次。

例如,如果您有一个函数 运行 每 1 秒使用 setInterval,但是由于 XHR 请求较慢,该函数本身需要 2 秒才能完成,该函数将同时 运行 两次时间在某个时候。这通常是不希望的。通过使用 setTimout 并在原始函数的末尾调用它,函数永远不会重叠,您设置的超时始终是两个函数调用之间的时间。

但是,在您的情况下,您的应用程序似乎很长-运行,因为您的函数每 3 秒运行一次,函数调用堆栈将每 3 秒增加 1。除非你打破这个递归循环,否则这是无法避免的。例如,您只能在收到浏览器事件(例如单击文档并检查时间)时才执行请求。

(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()。

编辑:上面的回答在某些方面是错误的

正如评论中指出的那样,setTimeout() 确实不会增加调用堆栈大小,因为它 returns 在调用超时函数之前。此外,问题中的函数不包含任何特定的递归。我会保留这个答案,因为部分问题是关于 setTimeout() 与 setInterval() 的。但是,导致递归错误的问题可能出在其他一些代码中,因为示例代码中的任何地方都没有直接或间接调用自身的函数。