Несколько запросов AJAX задерживают друг друга

У меня есть длинный запрос опросе на моей странице. Сценарий на стороне сервера имеет значение timeout через 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 ответов


Если вы используете сеансы в функциях обработки AJAX, вы столкнетесь с проблемой, когда данные сеанса диска блокируются первым запросом, поэтому каждый последующий запрос заканчивается ожиданием данных сеанса, которые будут доступны до его продолжения. По сути, это делает асинхронные вызовы блокирующими друг друга, вы получаете линейные ответы на запросы в хронологическом порядке - синхронные. (вот ссылка на статью)

решение заключается в использовании session_write_close (docs), чтобы закрыть сессию, как только вам это больше не нужно. Это позволяет выполнять другие последующие запросы, поскольку данные сеанса будут "разблокированы".

Это, однако, может запутаться, а также. Если вы позвоните session_write_close прямо перед тем, как вы вернете ответ, вы не собираетесь делать себе никаких одолжений, потому что сеанс был бы разблокирован, как только ответ был отправлен. Таким образом, его нужно вызвать как можно раньше. Если вы используете архитектура стиля post-back для AJAX-запроса это не так уж плохо, но если у вас есть большая платформа, и ваш обработчик запросов является только ее частью, вам придется изучить более высокое решение для неблокирующего использования сеанса, чтобы ваши подкомпоненты не закрывали сеанс, который ожидает платформа, все еще открыт.

один маршрут должен идти с сеансом базы данных. Есть плюсы и минусы этого решения, которые выходят за рамки этого ответа-проверьте 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 запроса-браузеры разрешают только два одновременных соединения с одним и тем же хостом в любой момент времени. Это, как было сказано, вы должны быть в порядке с длинным опросом (получать) и отправлять канал. Вы начинаете длинный опрос после загрузки страницы с помощью $(function () {...? Вы уверены, что запрос задерживается на клиенте, а не в браузере? Что вы видите в Firebug?


одна вещь, которую вы можете сделать, вы можете прервать запущенный опрос и сначала запустить запрос, а затем снова запустить опрос.

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