Подтвердить что ты не робот

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

У меня есть длинный запрос опроса на моей странице. script на стороне сервера устанавливается на таймаут через 20 секунд.

Итак, когда длительный опрос "холостого хода" и пользователь нажимает другую кнопку, отправка этого нового запроса задерживается до истечения предыдущего script.

Я не вижу ничего плохого в коде на стороне 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';
}

Может ли проблема "спать"?

XHR Screenshot

4b9b3361

Ответ 1

Если вы используете сеансы в функциях обработки 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

Это звучит в соответствии с 2 правилами запроса - браузеры разрешают только два одновременных подключения к одному и тому же хосту в любой момент времени. Это было сказано, вы должны быть в порядке с длинным опросом (получить) и отправить канал. Вы начинаете длительный опрос после загрузки страницы с помощью $(function() {...? Вы уверены, что запрос задерживается на клиенте, а не в браузере? Что вы видите в firebug?

Ответ 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()
           }
    });
}