Chrome: délais d'expiration / intervalle suspendu dans les onglets d'arrière-plan?

130

Je testais l'exactitude de l' setTimeoututilisation de ce test . Maintenant, j'ai remarqué que (comme prévu) ce setTimeoutn'est pas très précis, mais pour la plupart des appareils pas très inexact. Maintenant, si j'exécute le test dans Chrome et que je le laisse s'exécuter dans un onglet d'arrière-plan (donc, passer à un autre onglet et y naviguer), revenir au test et inspecter les résultats (si le test est terminé), ils sont considérablement modifiés. Il semble que les délais d'attente aient été beaucoup plus lents. Testé dans FF4 ou IE9, cela ne s'est pas produit.

Il semble donc que Chrome suspende ou au moins ralentisse l'exécution de JavaScript dans un onglet sans focus. Impossible de trouver grand-chose sur le net sur le sujet. Cela signifierait que nous ne pouvons pas exécuter de tâches en arrière-plan, comme par exemple la vérification périodique sur un serveur utilisant des appels XHR et setInterval(je soupçonne de voir le même comportement pour setInterval, écrira un test si le temps est avec moi).

Quelqu'un at-il rencontré cela? Y aurait-il une solution de contournement pour cette suspension / ralentissement? Appelleriez-vous cela un bogue et devrais-je le déclarer comme tel?

KooiInc
la source
Intéressant! Pouvez-vous dire si Chrome met en pause et reprend le minuteur ou le redémarre, une fois que vous accédez à nouveau à l'onglet? Ou le comportement est-il aléatoire? Cela pourrait-il avoir quelque chose à voir avec le fait que Chrome exécute des onglets dans des processus indépendants?
HyderA
@gAMBOOKa: jetez un œil à la réponse de @ pimvdb. C'est probablement un ralentissement jusqu'à un maximum d'une fois par seconde.
KooiInc
4 ans plus tard et ce problème existe toujours. J'ai un setTimeOut pour les divs avec un transition, donc pas toutes les divs transitions en même temps, mais en fait 15ms après l'autre, créant un effet de roulement. Lorsque je vais dans un autre onglet et que je reviens après un certain temps, toutes les divs font la transition en même temps et le setTimeOutest complètement ignoré. Ce n'est pas un gros problème pour mon projet, mais c'est un ajout étrange et indésirable.
Rvervuurt
Pour notre animation qui a appelé setTimeout dans une séquence, la solution pour nous était simplement de nous assurer que nous nous souvenions du handle / ID de la minuterie (il est retourné de setTimeout) et avant de définir une nouvelle minuterie, nous appelons d'abord clearTimeout si nous obtenu la poignée. Dans notre cas, cela signifie que lorsque vous revenez à l'onglet, il peut y avoir une certaine bizarrerie initiale en termes d'animation en cours de lecture, mais elle se trie assez rapidement et l'animation régulière reprend. Nous avions pensé que c'était un problème avec le code au départ.
Action Dan

Réponses:

88

J'ai récemment posé une question à ce sujet et c'est le comportement par conception. Lorsqu'un onglet est inactif, la fonction est appelée au maximum une fois par seconde. Voici le changement de code .

Peut-être que cela aidera: Comment puis-je faire en sorte que setInterval fonctionne également lorsqu'un onglet est inactif dans Chrome?

TL; DR: utilisez Web Workers .

pimvdb
la source
3
merci, j'aurais dû regarder avec «onglet inactif». Ne pas être anglophone est parfois un handicap.
KooiInc
1
@Kooilnc: Pas de problème :) Je ne suis pas non plus anglophone.
pimvdb
22

Il existe une solution pour utiliser les Web Workers, car ils s'exécutent dans un processus séparé et ne sont pas ralentis

J'ai écrit un petit script qui peut être utilisé sans modification de votre code - il remplace simplement les fonctions setTimeout, clearTimeout, setInterval, clearInterval

Incluez-le juste avant tout votre code

http://github.com/turuslan/HackTimer

Ruslan Tushov
la source
7
C'est bien, mais sachez que: 1. Les ouvriers n'ont pas accès au DOM, 2. Les ouvriers ne sont exécutés que s'ils sont seuls sur un fichier. Ce n'est pas un remplacement instantané de setTimeout dans de nombreux cas.
Madara's Ghost le
1
Vous avez raison, mais certains navigateurs modernes permettent d'utiliser des workers sans leurs propres fichiers en utilisant des Blobs ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
Ruslan Tushov
1
Même avec cela, les Web Workers manquent de nombreuses fonctionnalités (à savoir, DOM) qui leur permettent de remplacer en toute sécurité setTimeout et co.
Madara's Ghost le
Qu'en est-il du code qui doit s'exécuter dans le front-end, par exemple, des tâches de traitement graphique lourdes que nous aimerions terminer pendant que nous faisons d'autres choses?
Michael le
Eh bien, vous pouvez créer des travailleurs, des travailleurs de service et utiliser l'API de canevas à l'aide d'une URL de données. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'). C'est aussi un bon moyen de vérifier si vous avez le support des expressions d'importation:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
Fábio Santos
9

Jouer un son ~ vide oblige le navigateur à conserver les performances - je l'ai découvert après avoir lu ce commentaire: Comment faire fonctionner JavaScript à vitesse normale dans Chrome même lorsque l'onglet n'est pas actif?

J'ai besoin de performances illimitées à la demande pour un jeu par navigateur qui utilise WebSockets, donc je sais par expérience que l'utilisation de WebSockets ne garantit pas des performances illimitées, mais d'après les tests, la lecture d'un fichier audio semble le garantir.

Voici 2 boucles audio vides que j'ai créées à cet effet, vous pouvez les utiliser librement, commercialement: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Ils incluent un bruit de -58 dB, -60 dB ne fonctionne pas)

Je les joue, à la demande des utilisateurs, avec Howler.js: https://github.com/goldfire/howler.js

function performance_trick()
{
    if(sounds.empty) return sounds.empty.play();
    sounds.empty = new Howl({
        src: ['/sounds/loops/empty_loop_for_js_performance.ogg','/sounds/loops/empty_loop_for_js_performance.wav'],
        volume:0.5,
        autoplay: true, loop: true,
    });
}

Il est triste qu'il n'y ait pas de méthode intégrée pour activer / désactiver les performances JavaScript complètes par défaut, mais les crypto-mineurs peuvent détourner tous vos threads informatiques à l'aide de Web Workers sans aucune invite: |

Kaan Soral
la source
Merci, 58db est très audible avec des écouteurs, mais la mise en sourdine du site résout ce problème
Kaan Soral
1

J'ai publié le package npm à intervalle de travail qui implémente setInterval et clearInterval avec l'utilisation de Web-Workers pour rester opérationnel sur les onglets inactifs pour Chrome, Firefox et IE.

La plupart des navigateurs modernes (Chrome, Firefox et IE), les intervalles (minuteries de fenêtre) sont fixés pour ne pas se déclencher plus d'une fois par seconde dans les onglets inactifs.

Vous pouvez trouver plus d'informations sur

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Timeouts_and_intervals

gorkemcnr
la source
0

J'ai mis à jour mon noyau jQuery vers la version 1.9.1 et cela a résolu la différence d'intervalle dans les onglets inactifs. J'essaierais d'abord cela, puis j'examinerais d'autres options de remplacement de code.

Carey Estes
la source
de quelle version avez-vous mis à niveau? J'ai rencontré des problèmes de temporisation (curseurs de galerie) avec la version ~ 1.6
dmi3y
0

voici ma solution qui obtient la milliseconde actuelle, et la compare à la milliseconde que la fonction a été créée. pour l'intervalle, il mettra à jour la milliseconde lorsqu'il exécutera la fonction. vous pouvez également saisir l'intervalle / le délai d'expiration par un identifiant.

<script>

var nowMillisTimeout = [];
var timeout = [];
var nowMillisInterval = [];
var interval = [];

function getCurrentMillis(){
    var d = new Date();
    var now = d.getHours()+""+d.getMinutes()+""+d.getSeconds()+""+d.getMilliseconds();
    return now;
}

function setAccurateTimeout(callbackfunction, millis, id=0){
    nowMillisTimeout[id] = getCurrentMillis();
    timeout[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisTimeout[id] + +millis)){callbackfunction.call(); clearInterval(timeout[id]);} }, 10);
}

function setAccurateInterval(callbackfunction, millis, id=0){
    nowMillisInterval[id] = getCurrentMillis();
    interval[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisInterval[id] + +millis)){callbackfunction.call(); nowMillisInterval[id] = getCurrentMillis();} }, 10);
}

//usage
setAccurateTimeout(function(){ console.log('test timeout'); }, 1000, 1);

setAccurateInterval(function(){ console.log('test interval'); }, 1000, 1);

</script>
SwiftNinjaPro
la source