13

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

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?

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?

XHR Screenshot

F.P
  • 17,421
  • 34
  • 123
  • 189

3 Answers3

26

If you use sessions in the AJAX handling functions, you may run in to an issue where the server is maintaining session data on disk. If so, the data may be locked by the first request, so each subsequent request ends up waiting for the session data file 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)

A PHP-specific 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". Other server-side languages manage sessions in different ways, but this is usually something you can manage or control through some mechanism.

Managing sessions can have some pitfalls. If you call session_write_close (or otherwise end a session) 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.In smaller projects, this isn't so bad because you often have a php script that just handles the request and dumps a response, 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.

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 for your particular server-side language. 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 using PHP as an example:

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;
}
Chris Baker
  • 49,926
  • 12
  • 96
  • 115
  • 1
    I didn't exactly used your proposed solution, but the hint with `session_write_close()` was perfect! My requests were using sessions, and disabling them worked perfectly fine. – F.P Aug 04 '11 at 12:49
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?

Justin Beckwith
  • 7,686
  • 1
  • 33
  • 55
  • Yes, I am sure, because the request doesn't even show up in firebug for seconds. But when the polling ends and starts from the beginning, the request shows up, just like in the screenshot above. – F.P Aug 02 '11 at 05:16
  • I tested the AJAX alone, without the polling, and it took around 800ms to complete. When the polling is running, it always takes more than 2 seconds. – F.P Aug 02 '11 at 08:01
  • I agree here. Although newer browsers allow more (FF allows 6 and IE 8 allows 8 I guess). – Mrchief Aug 03 '11 at 20:46
  • As has been mentioned (sort of), this issue is mainly related to IE 8 and below (http://weblogs.asp.net/mschwarz/archive/2008/07/21/internet-explorer-8-and-maximum-concurrent-connections.aspx) As mentioned in the link, however, the HTTP spec suggests that clients **should** limit the number of concurrent connections. This is why a CDN is suggested - the CDN is a different domain name and thus allows you to bypass the concurrency restrictions of user agents. All that said, I don't think that is entirely the OP's problem :) – Chris Baker Aug 03 '11 at 21:13
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()
           }
    });
}
Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106