5

我正在编写一个 web 应用程序(仅与 Firefox 兼容),它使用长轮询(通过 jQuery 的 ajax 功能)将或多或少的持续更新从服务器发送到客户端。我担心长时间运行它的影响,比如一整天或一夜之间。基本的代码骨架是这样的:

function processResults(xml)
{
    // do stuff with the xml from the server
}

function fetch()
{
    setTimeout(function ()
    {
        $.ajax({
            type: 'GET',
            url: 'foo/bar/baz',
            dataType: 'xml',
            success: function (xml)
            {
                processResults(xml);
                fetch();
            },
            error: function (xhr, type, exception)
            {
                if (xhr.status === 0)
                {
                console.log('XMLHttpRequest cancelled');
                }
                else
                {
                    console.debug(xhr);
                    fetch();
                }
            }
        });
    }, 500);
}

(半秒的“睡眠”是为了让客户端在更新快速返回客户端时不会敲击服务器——它们通常是这样。)

在让它运行一夜之后,它往往会使 Firefox 爬行。我一直认为这可能部分是由较大的堆栈深度引起的,因为我基本上已经编写了一个无限递归函数。但是,如果我使用 Firebug 并将断点放入fetch,看起来情况并非如此。Firebug 向我展示的堆栈只有大约 4 或 5 帧深,即使在一小时后也是如此。

我正在考虑的解决方案之一是将递归函数更改为迭代函数,但我不知道如何在不旋转的情况下在 Ajax 请求之间插入延迟。我查看了JS 1.7“yield”关键字,但我无法完全理解它,以确定它是否是我需要的。

最好的解决方案是定期对页面进行硬刷新,比如每小时一次吗?是否有更好/更精简的长轮询设计模式,即使在运行 8 或 12 小时后也不会对浏览器造成伤害?或者我应该完全跳过长轮询并使用不同的“不断更新”模式,因为我通常知道服务器多久会回复我一次?

4

4 回答 4

2

我怀疑内存从processResults().

我一直在一个长轮询 Web 应用程序中使用与您的代码非常相似的代码,该应用程序能够连续数周不间断地运行而无需刷新页面。

你的堆栈不应该很深,因为会fetch()立即返回。您没有无限递归循环。

您可能希望使用 Firefox Leak Monitor插件来帮助您查找内存泄漏。

于 2010-01-19T16:08:03.430 回答
2

也有可能是 FireBug。你是 console.logging 的东西,这意味着你可能打开了一个网络监视器选项卡,等等,这意味着每个请求都存储在内存中。

尝试禁用它,看看是否有帮助。

于 2010-04-01T04:08:32.943 回答
1

4-5的堆栈深度是正确的。setTimeout并且$.ajax是异步调用,立即返回。该回调稍后由浏览器调用,调用堆栈为空。由于不能以同步方式实现长轮询,因此必须使用这种递归方法。没有办法让它迭代。

我怀疑这种变慢的原因是您的代码存在内存泄漏。泄漏可能$.ajax来自 jQuery(不太可能)或您的processResults电话。

于 2010-01-19T16:20:19.410 回答
1

fetch()从方法本身内部调用是个坏主意。当您期望方法在某个时候结束并且结果将开始发送给调用者时,最好使用递归。问题是,当您递归调用该方法时,它会使调用者方法保持打开状态并使用内存。如果你只有 3-4 帧深,那是因为 jQuery 或浏览器以某种方式“修复”了你所做的事情。

最新版本的 jquery 默认支持长轮询。这样你就可以确定你不会依赖浏览器的智能来处理你的无限递归调用。调用该$.ajax()方法时,您可以使用下面的代码进行长时间轮询,并在新调用之前安全等待 500 毫秒。

function myLongPoll(){
    setTimeout(function(){
        $.ajax({
            type:'POST',
            dataType: 'JSON',
            url: 'http://my.domain.com/action',
            data: {},
            cache: false,
            success:function(data){

                //do something with the result

            },
            complete: myLongPoll, 
            async : false,
            timeout: 5000
        });
   //Doesn't matter how long it took the ajax call, 1 milisec or 
   //5 seconds (timeout), the next call will only happen after 2 seconds
   }, 2000);

这样,您可以确保$.ajax()在下一个呼叫开始之前关闭呼叫。这可以通过在您的呼叫console.log()之前和之后添加一个简单的来证明。$.ajax()

于 2014-05-14T14:20:59.957 回答