javascript - jquery setTimeout 太多递归

标签 javascript jquery recursion settimeout setinterval

我从多个地方了解到,当设置某些东西基本上永远运行时,setTimeout() 比 setInterval() 更可取。下面的代码工作正常,但在运行 Firefox (38.0.1) 大约一个小时后,会抛出太多递归错误。

本质上,我让它从 counts.php 中获取非常少量的文本,并使用该信息更新表格。据检查人员称,整个调用和返回大约需要50ms。我试图让它按照 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() 的问题是,如果您想清除超时,则必须保留引用,并且万一操作(可能)执行时间比超时本身更长,则操作可能会运行两次。

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

但是,在您的情况下,您似乎有一个长时间运行的应用程序,因为您的函数每 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() 确实不会增加调用堆栈大小,因为它在调用超时内的函数之前返回。此外,问题中的函数不包含任何特定的递归。我会保留这个答案,因为问题的一部分是关于 setTimeout() 与 setInterval() 的。但是,导致递归错误的问题可能出现在其他代码段中,因为示例代码中的任何位置都没有直接或间接调用自身的函数。

关于javascript - jquery setTimeout 太多递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30444224/

相关文章:

javascript - JS 对象字面量中的方法和子方法

javascript - 如果编辑器的宽度太小,是否有办法将工具栏按钮换行到 TinyMCE 的下一行?

javascript - 使用ajax插入失败

Java - 简单图形和递归

java - 给定斐波那契递归函数创建内存算法

ruby-on-rails - 在 Ruby 中迭代深度嵌套的哈希级别

javascript - jQuery Flexigrid 调整 IE7 中的列大小已损坏

javascript - 将字符串拆分为 n 段并忽略换行符?

javascript - 在用户与窗口交互之前,Tampermonkey 脚本点击事件什么都不做

php - AJAX "load more"按钮 : disappears once clicked 问题