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

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

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. Но телефон не подключен к зарядному устройству. Я не видел его ни на каких планшетах, на яблочных устройствах или Windows-ПК.

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

     timeout: 120 * 1000,

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

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

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

Ответ 1

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

$(window).focus(function() {
    updater();
});

Ответ 2

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

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

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

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

Window.setInterval (refreshCall(), 10000);

function refreshCall (){
    myCall.abort ();
    myCall = $.ajax({...});
}

Ответ 3

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

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). фокус срабатывает, когда телефон просыпается, поэтому вы пытаетесь обновить(), как предлагает Алмис, но с таймером с отказоустойчивостью. Если обновляется программа обновления (на ноутбуках или iOS), таймер отменяется, и все в порядке, но если программа обновления мертва, таймер отказоустойчивости срабатывает через 6 секунд и перезагружает все ваше приложение, вызывая initializeAll().

Ответ 4

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

Ответ 5

Лучший ответ - "не делай этого". У вас есть сервер, ожидающий ответа, когда он отслеживает изменения на стороне сервера. Просто ответьте на сервер и выполните ping 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);