多个AJAX请求相互延迟

时间:2011-08-01 19:25:10

标签: jquery ajax xmlhttprequest long-polling

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

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

我看不出jQuery方面的代码有什么问题。为什么onclick事件会延迟?

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';
}

“usleep”可能成为问题吗?

XHR Screenshot

3 个答案:

答案 0 :(得分:25)

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

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

管理会话可能会有一些陷阱。如果您在返回响应之前立即致电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;
}

答案 1 :(得分:1)

这听起来与2请求规则一致 - 浏览器在任何给定时间只允许两个并发连接到同一主机。话虽如此,你应该对长轮询(接收)和发送频道都没问题。你是否在使用$(function(){...?页面加载后开始长轮询?你确定请求在客户端上被延迟了,而不是在浏览器中吗?你在firebug中看到了什么?

答案 2 :(得分:1)

您可以做的一件事是,您可以中止正在运行的民意调查并先运行您的请求,然后再次开始民意调查。

//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()
           }
    });
}