jQuery.ajax() с длинным опросом не может выполнить обратный вызов после того, как телефон спит?

Мое веб-приложение использует метод «длинного опроса», чтобы быть в курсе последних данных с моего сервера. Сервер отвечает только тогда, когда у него есть новые данные, которые могут отличаться друг от друга на много минут. (Это система управления отоплением, в которой вы видите обновления только при изменении температуры в помещении или при изменении настроек).

var version = "0";
function updater() {
    $.ajax({
        type: "POST",
        url: "/listen",
        data: version,
        success: function (data) {
            version = handleUpdates(data);
            updater();
        },
        error: function () {
            setTimeout(updater, 1000);
        }
    });
}

Он отлично работает в настольных браузерах и на телефонах, за исключением одного случая. Я обнаружил, что на телефонах Android с Chrome происходит что-то странное после того, как телефон перешел в спящий режим более чем на 10 минут. Почтовый запрос, кажется, отброшен, что, я думаю, разумно, поскольку телефон спит. На вкладке Сеть отладчика Chrome в тексте состояния запроса POST указано (отменено).

Проблема заключается в том, что когда я просыпаю телефон, когда запрос отменен, ни функции success(), ни error() не вызываются, и мое веб-приложение никогда не обновляется. $.ajax() нарушил свое обещание перезвонить мне.

Проблема возникает только на некоторых устройствах. Я смог провести несколько специальных тестов, позаимствовав устройства у друзей. Пока я видел проблему только на телефонах Android. Но телефон не подключен к зарядному устройству. Я не видел его ни на каких планшетах, на устройствах Apple или ПК с Windows.

Я попытался добавить тайм-аут в настройки ajax:

     timeout: 120 * 1000,

Это помогает, потому что функция error() в конечном итоге вызывается через 2 минуты после пробуждения. Но я бы хотел, чтобы пользователь видел обновления в течение 1 или 2 секунд. Я не хочу делать тайм-аут таким коротким, потому что это создаст ненужный серверный трафик.

Я также попытался определить, находится ли устройство в спящем режиме, проверив задержку в setInterval в одну секунду, как описано в разделе Могут ли какие-либо настольные браузеры определить, когда компьютер выходит из спящего режима? ?. Когда я обнаруживаю пробуждение, я прерываю пост и начинаю другой. Это помогает в большинстве случаев. Но оказывается ненадежным. Иногда кажется, что временные события продолжают нормально тикать во время сна, и запрос на публикацию все равно отменяется. И это не похоже на надежное решение.

Я использую последнюю версию jQuery: (2.1.2) и Chrome (47).


person James    schedule 17.12.2015    source источник
comment
попробуйте сохранить текущее обещание глобально, а затем, когда вы войдете в это неудачное состояние, проверьте .state() обещания. Что он содержит? Если он содержит что-либо, кроме ожидающих, вы можете использовать его в сочетании с решением @spozun для запуска резервного копирования после периода сна без ненужной отмены запросов.   -  person Kevin B    schedule 22.12.2015


Ответы (5)


В прошлом у меня были проблемы с приостановкой вызовов JavaScript, когда телефон переходил в спящий режим. Решение, к которому я пришел, заключалось в том, чтобы использовать window.setInterval(), который, кажется, приостанавливается, но возвращается к жизни, когда телефон просыпается.

Поэтому я бы рекомендовал установить интервал, который время от времени отменяет вызов и возобновляет его. Это может помочь ему пережить сон телефона.

Что-то примерно такое:

var myCall = $.ajax({...});

Window.setInterval (refreshCall(), 10000);

function refreshCall (){
    myCall.abort ();
    myCall = $.ajax({...});
}
person spozun    schedule 21.12.2015

Я не уверен, что это сработает или нет, я не могу проверить это сейчас, но попробуйте

$(window).focus(function() {
    updater();
});
person Almis    schedule 17.12.2015
comment
Разве это не вызовет другой запрос в тех случаях, когда браузер работает должным образом и оставляет запрос открытым? - person Katana314; 17.12.2015
comment
@Katana314 это сработает только в том случае, если пользователь был на другой вкладке (полагаю, это также работает для неактивных на мобильных устройствах) и сфокусировался на моей вкладке. Конечно, вы также можете использовать какой-либо флаг, чтобы увидеть, работает ли программа обновления должным образом. - person Almis; 17.12.2015
comment
К сожалению, это не работает. Оно попробовало. $(window).focus() не вызывает функцию, когда телефон выходит из спящего режима. Кажется, что функция вызывается только при переключении между вкладками в Chrome. - person James; 17.12.2015
comment
@Almis Спасибо, я очень ценю ваши усилия. Я пробовал вещи в течение нескольких дней, и это была новая идея. - person James; 17.12.2015
comment
@Джеймс, я не смог воспроизвести вашу проблему, я установил демонстрационное приложение на своем сервере, которое просто задерживается на 5 секунд, прежде чем дать ответ, затем я свернул браузер, перевел мобильное устройство в спящий режим и вернулся в свой браузер, и он все еще работал ... проверено на хроме 47 - person Almis; 18.12.2015

Как насчет функции наблюдателя более высокого уровня, подобной этой:

var restartTimer = null;
function updater(){
    $.ajax({
        type: "POST",
        url: "/listen",
        data: version,
        success: function (data) {
            version = handleUpdates(data);
            clearTimeout(restartTimer);
            updater();
        },
        error: function () {
            clearTimeout(restartTimer);
            setTimeout(updater, 1000);
        }
    });
}
//  Kick it when the phone wakes up.
$(window).focus(function(){
    restartTimer = setTimeout(function(){
        initializeAll();
    }, 6000);
    updater();
});

Вы знаете, что $(window).focus сработает, когда телефон проснется, поэтому попробуйте updater(), как предлагает Алмис, но с безотказным таймером. Если программа обновления срабатывает (на ноутбуках или iOS), таймер отменяется, и все в порядке, но если программа обновления мертва, через 6 секунд срабатывает отказоустойчивый таймер и перезагружает все ваше приложение, вызывая initializeAll().

person Rob    schedule 26.10.2016

Как насчет setInterval, который сохраняет время своего вызова, а затем сравнивает последний раз, когда он был вызван, с текущим временем - если ваш интервал составляет 10 секунд, а время, прошедшее с момента последнего запуска, составляло 5 минут, вы можете предположить, что вы только что проснулся ото сна? Затем прервите текущий вызов ajax и перезапустите его.

person Michael Baldry    schedule 07.12.2017

Лучший ответ: «Не делай этого». Вы заставляете сервер ждать ответа, пока он отслеживает изменения на стороне сервера. Просто попросите сервер ответить и пинговать jQuery с интервалом. Вы можете включить lastchanged или haschanged, если хотите предотвратить фактическое обновление, когда нет изменения статуса, но если сервер в любом случае выполняет ту же работу, просто дайте ему ответить и дождитесь следующего опроса.

setInterval(function () { 
    $.post({"/listen", version, function (data) {
        if(data.haschanged)
            version = handleUpdates(data);
    }).fail(function () {
        // any error correction on failed call
    });
}, 1000);
person Veritoanimus    schedule 05.01.2018