Qu'arrive-t-il à L'exécution JavaScript (settimeout, etc.) quand iPhone / Android va dormir?

j'ai une application Web Mobile jQuery qui cible les appareils iOS et Android. Une composante de la demande est une tâche de fond, qui vérifie périodiquement pour A.) changements aux données locales et B.) connexion au serveur. Si les deux sont vrais, la tâche pousse les changements.

j'utilise une fonction simple basée sur setTimeout () pour exécuter cette tâche. Chaque condition d'échec ou de succès appelle setTimeout () sur la tâche background, en s'assurant qu'elle fonctionne à intervalles de 30 secondes. Je mets à jour un statut div avec l'horodatage de la dernière tâche runtime pour les besoins du débogage.

dans n'importe quel navigateur de bureau, cela fonctionne très bien; cependant, sur iOS ou Android, après un certain temps, la tâche cesse d'exécuter. Je me demande si cela est lié aux paramètres de conservation d'énergie des appareils--quand iOS entre en stand-by, Est-ce que cela interrompt L'exécution JavaScript? C'est ce qui semble arriver.

Si oui, quelle est la meilleure façon de le reprendre? Y a-t-il un événement de réveil que je peux accrocher dans? Si ce n'est pas le cas, quelles sont les autres options qui n'impliquent pas l'accrochage à des événements dépendant de l'interaction de l'Utilisateur (Je ne veux pas lier la page entière à un événement de clic juste pour redémarrer la tâche d'arrière-plan).

34
demandé sur Christopher 2011-07-01 07:36:27

3 réponses

on dirait que L'exécution Javascript est interrompue sur MobileSafari quand la page du navigateur n'est pas focalisée. Il semble aussi que si les événements setInterval() sont en retard, ils sont simplement lancés dès que le navigateur est focalisé. Cela signifie que nous devrions être en mesure de garder un setInterval() en cours d'exécution, et supposer que le navigateur a perdu/retrouvé la mise au point si la fonction setInterval a pris beaucoup plus de temps que d'habitude.

ce code alerte après avoir changé d'onglet du navigateur, après être revenu d'une autre application, et après reprise du sommeil. Si vous définissez votre seuil un peu plus longtemps que votre setTimeout(), vous pouvez supposer que votre timeout ne finira pas si cela se déclenche.

si vous voulez rester du côté de la sécurité: Vous pouvez enregistrer votre ID de timeout (retourné par setTimeout) et le mettre à un seuil plus court que votre timeout, puis lancer clearTimeout() et setTimeout() de nouveau si cela se déclenche.

<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>

Edit: il semble que cela peut parfois déclencher plus d'une fois dans une rangée sur le CV, donc vous avez besoin de poignée en quelque sorte. (Après avoir laissé mon navigateur android dormir toute la nuit, il s'est réveillé à deux alertes (). Je parie que Javascript a repris à un moment arbitraire avant de dormir complètement.)

j'ai testé sur Android 2.2 et le dernier iOS - ils alertent tous les deux dès que vous reprenez du sommeil.

27
répondu lunixbochs 2011-07-01 14:05:03

Lorsque l'utilisateur passe à une autre application ou l'écran est en veille, minuteries semblent pause jusqu'à ce que l'utilisateur revient à l'application (ou lorsque l'écran s'éveille).

Phonegap a un resume événement que vous pouvez écouter au lieu de l'interrogation de l'état (ainsi que d'un pause événement si vous voulez faire les choses avant qu'il est de se concentrer). Tu commences à l'écouter après que deviceReady ait tiré.

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

j'utilise angulaire avec phonegap et j'ai un service implémenté qui gère un certain timeout pour moi mais essentiellement vous pouvez créer un objet qui fixe le timer, annule le timer et surtout, met à jour le timer (update est ce qu'on appelle pendant l'événement 'resume').

en angle j'ai un scopes et une portée racine à laquelle je peux attacher des données, Mon timeout est global donc je l'attache à la portée racine mais pour les besoins de cet exemple, je vais simplement l'attacher à l'objet document. Je ne cautionne pas que parce que vous avez besoin doit l'appliquer à une sorte de champ ou de l'espace.

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;
            }
        }
    };
};

vous pouvez demander à la fonction manager de prendre un paramètre milliseconde au lieu de le passer dans set, mais encore une fois ceci est modelé un peu d'après le service angulaire que j'ai écrit. Les opérations doivent être suffisamment claires et concises pour faire quelque chose avec eux et les ajouter à votre propre application.

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

vous voudrez mettre à jour la minuterie une fois que vous aurez l'événement resume dans votre event listener, comme suit:

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

Le délai d'attente le gestionnaire a également cancelTimer() qui peut être utilisé pour arrêter la minuterie à tout moment.

4
répondu jlafay 2015-01-08 19:35:30

Vous pouvez utiliser cette classe github.com/mustafah/background-timer @jlafay réponse , où vous pouvez l'utiliser comme suit:

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();

J'espère que ça vous aidera, Merci ...

2
répondu Mustafah 2016-06-12 09:39:02