Несколько запросов AJAX задерживают друг друга - PullRequest
13 голосов
/ 01 августа 2011

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

Может ли "уснуть" быть проблемой?

XHR Screenshot

Ответы [ 3 ]

25 голосов
/ 04 августа 2011

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

Специфичное для PHP решение - использовать session_write_close ( docs ), чтобы закрыть сеанс, как только он вам больше не нужен. Это позволяет другим последующим запросам выполняться, потому что данные сеанса будут «разблокированы». Другие серверные языки управляют сессиями по-разному, но это обычно то, что вы можете управлять или контролировать с помощью какого-то механизма.

Управление сессиями может иметь некоторые подводные камни. Если вы позвоните 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 голос
/ 04 августа 2011

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

//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 голос
/ 02 августа 2011

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

...