多个 AJAX 请求相互延迟

Posted

技术标签:

【中文标题】多个 AJAX 请求相互延迟【英文标题】:Multiple AJAX requests delay each other 【发布时间】:2011-10-17 17:08:50 【问题描述】:

我的页面上有一个长轮询请求。服务器端的脚本设置为20秒后超时。

因此,当长轮询处于“空闲”状态,并且用户按下另一个按钮时,新请求的发送将延迟到前一个脚本超时。

我看不出 jQuery 端的代码有什么问题。为什么 onclick-event 会延迟?

function poll()

$.ajax(
    url: "/xhr/poll/1",
    data: 
        user_id: app.user.id
    ,
    type: "POST",
    dataType: "JSON",
    success: pollComplete,
    error: function(response) 
        console.log(response);
    
);


function pollComplete()

    poll();


function joinRoom(user_id)

$.ajax(
    url: "/xhr/room/join",
    dataType: "JSON",
    type: "POST",
    data: 
        user_id: app.user.id,
        room_id: room.id
    
);


<button id="join" onclick="javascript:joinRoom(2);">Join</button>

############ php Controller on /xhr/poll

$time = time();
while ((time() - $time) < 20)

    $updates = $db->getNewStuff();

    foreach ($updates->getResult() as $update)
        $response[] = $update->getResponse();

    if (!empty($response))
        return $response;
    else
        usleep(1 * 1000000);

    return 'no-updates';

“睡眠”会是问题吗?

【问题讨论】:

本地主机也有问题吗?? 任一 AJAX 调用的 PHP 代码是否使用会话? 【参考方案1】:

如果您在 AJAX 处理函数中使用会话,您可能会遇到服务器在磁盘上维护会话数据的问题。如果是这样,数据可能会被第一个请求锁定,因此每个后续请求最终都会等待会话数据文件可用,然后再继续。实际上,这会使异步调用相互阻塞,最终您会按时间顺序对请求进行线性响应 - 同步。 (here's a reference article)

一个特定于 PHP 的解决方案是使用 session_write_close (docs) 在您不再需要会话时立即关闭它。这允许其他后续请求继续进行,因为会话数据将被“解锁”。其他服务器端语言以不同的方式管理会话,但这通常是您可以通过某种机制来管理或控制的。

管理会话可能存在一些缺陷。如果您在返回响应之前致电session_write_close(或以其他方式结束会话),那么您不会帮自己任何忙,因为一旦发送响应,会话就会被解锁。因此,它需要尽早调用。在较小的项目中,这还不错,因为您通常有一个 php 脚本来处理请求并转储响应,但是如果您有更大的框架和请求处理程序只是其中的一部分,您必须探索更***的非阻塞会话使用解决方案,以便您的子组件不会关闭框架期望仍处于打开状态的会话。

一种方法是使用数据库会话。此解决方案的优缺点超出了此答案的范围 - 检查 Google 以获取针对您的特定服务器端语言的详尽讨论。另一种方法是使用一个函数来打开一个会话,添加一个变量,然后关闭它。使用此解决方案可能会出现竞争条件,但这里以 PHP 为例进行了粗略的概述:

function get_session_var($key, $default=null) 
    if (strlen($key) < 1)
        return null;
    if (!isset($_SESSION) || !is_array($_SESSION)) 
        session_start();
        session_write_close();
    
    if (array_key_exists($key, $_SESSION))
        return $_SESSION[$key];
    return $default;

function set_session_var($key, $value=null) 
    if (strlen($key) < 1)
        return false;
    if ($value === null && array_key_exists($key, $_SESSION)) 
        session_start();
        unset($_SESSION[$key]);
     elseif ($value != null) 
        session_start();
        $_SESSION[$key] = $value;
     else 
        return false;
    
    session_write_close();
    return true;

【讨论】:

我没有完全使用您提出的解决方案,但session_write_close() 的提示非常完美!我的请求正在使用会话,并且禁用它们效果很好。【参考方案2】:

这听起来与 2 请求规则一致 - 浏览器在任何给定时间只允许两个并发连接到同一主机。话虽如此,长轮询(接收)和发送通道应该没问题。您是否在使用 $(function()...

【讨论】:

是的,我敢肯定,因为请求甚至在几秒钟内都没有出现在萤火虫中。但是当轮询结束并从头开始时,请求就会出现,就像上面的屏幕截图一样。 我单独测试了 AJAX,没有轮询,大约需要 800 毫秒才能完成。轮询运行时,总是需要 2 秒以上。 我同意这里。虽然较新的浏览器允许更多(FF 允许 6 和 IE 8 允许 8 我猜)。 如前所述(有点),此问题主要与 IE 8 及以下版本有关 (weblogs.asp.net/mschwarz/archive/2008/07/21/…) 如链接中所述,但是,HTTP 规范建议客户端 应该 限制并发连接数。这就是为什么建议使用 CDN 的原因 - CDN 是一个不同的域名,因此可以让您绕过用户代理的并发限制。综上所述,我认为这不完全是 OP 的问题 :)【参考方案3】:

您可以做的一件事是,您可以中止正在运行的投票并首先运行您的请求,然后再次启动投票。

//make sure pollJqXhr.abort is not undefined
var pollJqXhr=abort:$.noop; 

function poll()

    //assign actual jqXhr object
    pollJqXhr=jQuery.ajax(youroptions);


function pollComplete()

   poll();



function joinRoom(user_id)

   //pause polling
   pollJqXhr.abort();

   jquery.ajax(
           /*options here*/
           success:function()
           
                /*Your codes*/

                //restart poll
                poll()
           
    );

【讨论】:

看来这是治标不治本

以上是关于多个 AJAX 请求相互延迟的主要内容,如果未能解决你的问题,请参考以下文章

jQuery Deferred - 等待多个 AJAX 请求完成 [重复]

利用Promise实现数据多个请求加载完成时执行某个方法

尝试向 jQuery AJAX 请求添加延迟

ajax请求响应过长怎么解决

javascript 延迟AJAX请求

jQuery ajax 请求作为延迟 - 包装失败回调