多个AJAX请求相互延迟

时间:2022-11-30 11:26:38

I have a long polling request on my page. The script on the server side is set to timeout after 20 seconds.

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

So, when the long polling is "idling", and the user presses another button, the sending of that new request is delayed until the previous script times out.

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

I can't see anything wrong with the code on jQuery side. Why is the onclick-event delayed?

我看不出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';
}

Could the "usleep" be the problem?

“usleep”可能成为问题吗?

多个AJAX请求相互延迟

3 个解决方案

#1


23  

If you use sessions in the AJAX handling functions, you will run in to an issue where the disk session data is locked by the first request, so each subsequent request ends up waiting for the session data to be available before it proceeds. In effect, this makes asynchronous calls block one another, you end up with linear responses to the requests in chronological order - synchronous. (here's a reference article)

如果在AJAX处理函数中使用会话,则会遇到第一个请求锁定磁盘会话数据的问题,因此每个后续请求都会在会话数据继续之前等待会话数据可用。实际上,这会使异步调用相互阻塞,最终会按时间顺序对请求进行线性响应 - 同步。 (这是一篇参考文章)

A solution is to use session_write_close (docs) to close out the session as soon as you don't need it any more. This allows other subsequent requests to proceed because the session data will be "unlocked".

解决方案是使用session_write_close(docs)在您不再需要会话时立即关闭会话。这允许其他后续请求继续进行,因为会话数据将被“解锁”。

This, however, can get confusing as well. If you call session_write_close right before you return a response, then you aren't going to do yourself any favors because the session would have been unlocked as soon as the response was sent. Thus, it needs to be called as early as possible. If you are using post-back style architecture for AJAX request, this isn't so bad, but if you have a larger framework and your request handler is only a part of it, you'll have to explore a more top-level solution to non-blocking session usage so your subcomponents are not closing a session that the framework expects is still open.

然而,这也会让人感到困惑。如果你在返回响应之前调用session_write_close,那么你不会给自己任何好处,因为一旦发送响应,会话就会被解锁。因此,需要尽早调用它。如果您正在使用回发式架构来处理AJAX请求,这不是很糟糕,但如果您有一个更大的框架并且您的请求处理程序只是其中的一部分,那么您将不得不探索更高级的解决方案非阻塞会话使用,因此您的子组件没有关闭框架期望仍然打开的会话。

One route is to go with database session. There are pros and cons to this solution which are beyond the scope of this answer - check Google for exhaustive discussion. Another route is to use a function that opens a session, adds a variable, then closes it. You risk race conditions with this solution, but here's a rough outline:

一种方法是使用数据库会话。这个解决方案的优点和缺点超出了这个答案的范围 - 请查看Google进行详尽的讨论。另一种方法是使用一个打开会话,添加变量然后关闭它的函数。您可以通过此解决方案冒险竞争条件,但这是一个粗略的概述:

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

#2


1  

This sounds consistent with the 2 request rule - browsers only allow two concurrent connections to the same host at any one given time. That having been said, you should be fine with a long poll (receive) and send channel. Are you starting the long poll after page load using $(function(){...? Are you sure the request is being delayed on the client, and not in the browser? What are you seeing in firebug?

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

#3


1  

One thing you can do, you can abort the running poll and run your request first and then again start the poll.

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

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

#1


23  

If you use sessions in the AJAX handling functions, you will run in to an issue where the disk session data is locked by the first request, so each subsequent request ends up waiting for the session data to be available before it proceeds. In effect, this makes asynchronous calls block one another, you end up with linear responses to the requests in chronological order - synchronous. (here's a reference article)

如果在AJAX处理函数中使用会话,则会遇到第一个请求锁定磁盘会话数据的问题,因此每个后续请求都会在会话数据继续之前等待会话数据可用。实际上,这会使异步调用相互阻塞,最终会按时间顺序对请求进行线性响应 - 同步。 (这是一篇参考文章)

A solution is to use session_write_close (docs) to close out the session as soon as you don't need it any more. This allows other subsequent requests to proceed because the session data will be "unlocked".

解决方案是使用session_write_close(docs)在您不再需要会话时立即关闭会话。这允许其他后续请求继续进行,因为会话数据将被“解锁”。

This, however, can get confusing as well. If you call session_write_close right before you return a response, then you aren't going to do yourself any favors because the session would have been unlocked as soon as the response was sent. Thus, it needs to be called as early as possible. If you are using post-back style architecture for AJAX request, this isn't so bad, but if you have a larger framework and your request handler is only a part of it, you'll have to explore a more top-level solution to non-blocking session usage so your subcomponents are not closing a session that the framework expects is still open.

然而,这也会让人感到困惑。如果你在返回响应之前调用session_write_close,那么你不会给自己任何好处,因为一旦发送响应,会话就会被解锁。因此,需要尽早调用它。如果您正在使用回发式架构来处理AJAX请求,这不是很糟糕,但如果您有一个更大的框架并且您的请求处理程序只是其中的一部分,那么您将不得不探索更高级的解决方案非阻塞会话使用,因此您的子组件没有关闭框架期望仍然打开的会话。

One route is to go with database session. There are pros and cons to this solution which are beyond the scope of this answer - check Google for exhaustive discussion. Another route is to use a function that opens a session, adds a variable, then closes it. You risk race conditions with this solution, but here's a rough outline:

一种方法是使用数据库会话。这个解决方案的优点和缺点超出了这个答案的范围 - 请查看Google进行详尽的讨论。另一种方法是使用一个打开会话,添加变量然后关闭它的函数。您可以通过此解决方案冒险竞争条件,但这是一个粗略的概述:

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

#2


1  

This sounds consistent with the 2 request rule - browsers only allow two concurrent connections to the same host at any one given time. That having been said, you should be fine with a long poll (receive) and send channel. Are you starting the long poll after page load using $(function(){...? Are you sure the request is being delayed on the client, and not in the browser? What are you seeing in firebug?

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

#3


1  

One thing you can do, you can abort the running poll and run your request first and then again start the poll.

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

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