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

Что происходит с выполнением JavaScript (settimeout и т.д.), Когда iPhone/Android ложится спать?

У меня есть веб-приложение jQuery Mobile, которое предназначено для устройств iOS и Android. Компонент приложения - это фоновая задача, которая периодически проверяет наличие.) Изменений в локальных данных и b) подключение к серверу. Если оба значения истинны, задача меняет изменения.

Я использую простую функцию setTimeout() для выполнения этой задачи. Каждое условие отказа или успеха вызывает setTimeout() в фоновом задании, гарантируя, что он работает с интервалом в 30 секунд. Я обновляю div состояния с отметкой времени последней задачи выполнения задачи для отладки.

В любом настольном браузере это работает отлично; однако на iOS или Android после определенного периода времени задача перестает выполняться. Мне интересно, связано ли это с настройками энергосбережения устройств - когда iOS входит в режим ожидания, завершает ли он выполнение JavaScript? Вот что происходит.

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

4b9b3361

Ответ 1

Похоже, что выполнение Javascript приостановлено на MobileSafari, когда страница браузера не сфокусирована. Также кажется, что если события setInterval() задерживаются, они просто запускаются, как только браузер фокусируется. Это означает, что мы должны поддерживать запуск setInterval() и предполагать, что браузер потерял/восстановил фокус, если функция setInterval заняла гораздо больше времени, чем обычно.

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

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

<script type="text/javascript">
var lastCheck = 0;

function sleepCheck() {
        var now = new Date().getTime();
        var diff = now - lastCheck;
        if (diff > 3000) {
                alert('took ' + diff + 'ms');
        }
        lastCheck = now;
}

window.onload = function() {
        lastCheck = new Date().getTime();
        setInterval(sleepCheck, 1000);
}
</script>

Изменить: Похоже, что это может иногда срабатывать несколько раз подряд при возобновлении, поэтому вам нужно как-то это обработать. (После того, как мой браузер андроида спал всю ночь, он проснулся до двух предупреждений(). Я уверен, Javascript был возобновлен в какое-то произвольное время до полного сна.)

Я тестировал на Android 2.2 и новейшую iOS - они оба предупреждают, как только вы возобновляете спящий режим.

Ответ 2

Когда пользователь переключается на другое приложение или экран спит, таймеры, кажется, останавливаются, пока пользователь не переключится обратно в приложение (или когда экран пробуждается).

В телефоне есть resume событие, которое вы можете прослушивать вместо опроса для состояния (а также pause, если вы хотите сделать что-то до того, как оно не в фокусе). Вы начинаете слушать его после запуска устройства.

document.addEventListener("deviceready", function () {
    // do something when the app awakens
    document.addEventListener('resume', function () {
        // re-create a timer.
        // ...
    }, false);
}, false);

Я использую angular с телефонной связью, и у меня есть служба, которая управляет определенным временем ожидания для меня, но в основном вы можете создать объект, который устанавливает таймер, отменяет таймер и, самое главное, обновляет таймер (обновление - это то, что вызывается во время события "resume" ).

В angular У меня есть область областей видимости и корня, к которой я могу прикреплять данные, мой тайм-аут является глобальным, поэтому я прикрепляю его к корневой области, но для целей этого примера я просто привяжу его к объекту документа, Я не согласен с этим, потому что вам нужно применить его к какой-то области или пространству имен.

var timeoutManager = function () {
    return {
        setTimer: function (expiresMsecs) {
            document.timerData = {
                timerId: setTimeout(function () {
                    timeoutCallback();
                },
                expiresMsecs),

                totalDurationMsecs: expiresMsecs,
                expirationDate: new Date(Date.now() += expiresMsecs)
            };
        },

        updateTimer: function () {
            if (document.timerData) {
                //
                //  Calculate the msecs remaining so it can be used to set a new timer.
                //
                var timerMsecs = document.timerData.expirationDate - new Date();

                //
                //  Kill the previous timer because a new one needs to be set or the callback
                //  needs to be fired.
                //
                this.cancelTimer();

                if (timerMsecs > 0) {
                    this.setTimer(timerMsecs);
                } else {
                    timeoutCallback();
                }
            }
        },

        cancelTimer: function () {
            if (document.timerData && document.timerData.timerId) {
                clearTimeout(document.timerData.timerId);
                document.timerData = null;
            }
        }
    };
};

Вы могли бы заставить диспетчеру использовать параметр миллисекунды вместо того, чтобы передавать его в набор, но опять-таки это несколько моделируется после службы angular, которую я написал. Операции должны быть четкими и лаконичными, чтобы что-то сделать с ними и добавить их в свое приложение.

var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);

Вам нужно будет обновить таймер, как только вы получите событие возобновления в своем прослушивателе событий, например:

// do something when the app awakens
document.addEventListener('resume', function () {
    var manager = timeoutManager();
    manager.updateTimer();
}, false);

В менеджере тайм-аута также есть cancelTimer(), который можно использовать для уничтожения таймера в любое время.

Ответ 3

Вы можете использовать этот класс github.com/mustafah/background-timer на основе ответа @jlafay, где вы можете использовать следующее:

CoffeeScript

timer = new BackgroundTimer 10 * 1000, ->
    # This callback will be called after 10 seconds
    console.log 'finished'

timer.enableTicking 1000, (remaining) ->
    # This callback will get called every second (1000 millisecond) till the timer ends
    console.log remaining

timer.start()

Javascript

timer = new BackgroundTimer(10 * 1000, function() {
    // This callback will be called after 10 seconds
    console.log("finished");
});

timer.enableTicking(1000, function(remaining) {
    // This callback will get called every second (1000 millisecond) till the timer ends
    console.log(remaining);
});

timer.start();

Надеюсь, это поможет, спасибо...

Ответ 4

Вам следует использовать Page Visibility API (MDN), который поддерживается практически везде. Он может определить, стала ли страница или вкладка снова видимой, и вы можете возобновить тайм-ауты или выполнить некоторые действия.