【问题标题】:Improving long-polling Ajax performance提高长轮询 Ajax 性能
【发布时间】:2011-01-06 21:06:50
【问题描述】:

我正在编写一个 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" keyword,但我无法完全理解它,以确定它是否是我需要的。

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

【问题讨论】:

    标签: javascript ajax performance long-polling


    【解决方案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() 调用之后添加另一个。

    【讨论】:

    • 使用 jQuery 的 complete 回调与我的问题中的实现没有太大区别。这不是 Java - 调用者永远不需要显式关闭 XHR 或其他资源。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      我怀疑内存从processResults() 泄漏。

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

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

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

      【讨论】:

      • 我认为这是另一种“打破”堆栈深度的方法,但我认为这与我原始代码中的 setTimeout 没有任何不同的效果。
      • 是的,但是你的堆栈不应该很深,因为 fetch() 立即返回,所以成功和错误函数很快就会离开堆栈。
      • 有可能。在 FF 中检测内存泄漏的最佳工具是什么?
      • 我刚刚意识到我已经很长时间没有尝试在禁用 Firebug 的情况下运行它了。这很可能是问题所在。
      • @Bears:可能是这样,但我仍然设法让 Firebug 的长轮询应用程序保持打开几天而没有刷新页面,也没有明显的内存泄漏。然而,萤火虫可能会因为processResults() 中的合法内容而泄漏内存,而我的应用程序没有这样做。所以是的,这可能是您的问题的原因。
      【解决方案4】:

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

      我怀疑速度变慢的原因是您的代码存在内存泄漏。泄漏可能在 jQuery 的 $.ajax 中(不太可能)或在您的 processResults 调用中。

      【讨论】:

        猜你喜欢
        • 2014-06-24
        • 2012-04-15
        • 2012-02-24
        • 2011-04-20
        • 2012-08-16
        • 1970-01-01
        • 2012-05-17
        • 2011-09-19
        • 1970-01-01
        相关资源
        最近更新 更多