Chrome: timeout / intervallo sospesi nelle schede in background?


130

Stavo testando l'accuratezza setTimeoutdell'uso di questo test . Ora ho notato che (come previsto) setTimeoutnon è molto preciso, ma per la maggior parte degli apparecchi non drammaticamente impreciso. Ora, se eseguo il test in Chrome e lo lascio eseguire in una scheda in background (quindi, passando a un'altra scheda e navigando lì), tornando al test e ispezionando i risultati (se il test è terminato), vengono cambiati radicalmente. Sembra che i timeout abbiano funzionato molto più lentamente. Testato in FF4 o IE9 questo non si è verificato.

Quindi sembra che Chrome sospenda o almeno rallenti l'esecuzione di JavaScript in una scheda che non ha alcun focus. Non ho trovato molto in rete sull'argomento. Significherebbe che non possiamo eseguire attività in background, come ad esempio controllare periodicamente su un server usando le chiamate XHR e setInterval(sospetto di vedere lo stesso comportamento per setInterval, scriverò un test se il tempo è con me).

Qualcuno ha riscontrato questo? Ci sarebbe una soluzione alternativa per questa sospensione / rallentamento? Lo chiameresti un bug e dovrei archiviarlo come tale?


Interessante! Puoi sapere se Chrome sta sospendendo e riprendendo il timer o riavviandolo, una volta che accedi nuovamente alla scheda? O il comportamento è casuale? Potrebbe avere qualcosa a che fare con il fatto che Chrome esegue le schede in processi indipendenti?
HyderA,

@gAMBOOKa: dai un'occhiata alla risposta di pimvdb. È probabile che rallenti fino a un massimo di una volta al secondo.
KooiInc,

4 anni dopo e questo problema esiste ancora. Ho un setTimeOut per i div con una transition, quindi non tutte le div div allo stesso tempo, ma in realtà 15ms dopo l'altro, creando un effetto rotolante. Quando vado in un'altra scheda e torno dopo un po ', tutte le div passano contemporaneamente e setTimeOutviene completamente ignorato. Non è un grosso problema per il mio progetto, ma è un'aggiunta strana e indesiderata.
Rvervuurt,

Per la nostra animazione che ha chiamato setTimeout in una sequenza, la soluzione per noi era solo quella di assicurarci di ricordare l'handle / ID del timer (è tornato da setTimeout) e prima di impostare un nuovo timer chiamiamo clearTimeout se abbiamo ottenuto la maniglia. Nel nostro caso ciò significa che quando torni alla scheda, potrebbe esserci una certa stranezza iniziale in termini di animazione che viene riprodotta, ma si risolve abbastanza rapidamente e riprende l'animazione normale. Inizialmente avevamo pensato che si trattasse di un problema con il codice esterno.
Azione Dan

Risposte:


88

Di recente ho chiesto di questo ed è un comportamento di progettazione. Quando una scheda è inattiva, solo al massimo una volta al secondo viene chiamata la funzione. Ecco la modifica del codice .

Forse questo aiuterà: come posso fare in modo che setInterval funzioni anche quando una scheda non è attiva in Chrome?

TL; DR: usa Web Workers .


3
grazie, avrei dovuto guardare con 'scheda inattiva'. Non essere un madrelingua inglese a volte è un handicap.
KooiInc,

1
@Kooilnc: nessun problema :) Non sono neanche un madrelingua inglese.
pimvdb,

22

Esiste una soluzione per utilizzare i Web Worker, poiché vengono eseguiti in processi separati e non vengono rallentati

Ho scritto un piccolo script che può essere utilizzato senza modifiche al codice: sostituisce semplicemente le funzioni setTimeout, clearTimeout, setInterval, clearInterval

Inseriscilo prima di tutto il tuo codice

http://github.com/turuslan/HackTimer


7
È bello e tutto, ma tieni presente che: 1. I lavoratori non hanno accesso al DOM, 2. I lavoratori vengono sempre eseguiti solo se si trovano su un file da soli. E ' non è un rimpiazzo per setTimeout per un sacco di casi.
Madara's Ghost

1
Hai ragione, ma alcuni browser moderni consentono di utilizzare i lavoratori senza i propri file utilizzando Blobs ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
Ruslan Tushov

1
Nonostante ciò, ai Web Worker mancano molte funzionalità (vale a dire DOM) che consentono loro di essere un sostituto sicuro di setTimeout e co.
Madara's Ghost

Che dire del codice che deve essere eseguito nel front-end, ad esempio, pesanti attività di elaborazione grafica che vorremmo finire mentre facciamo altre cose?
Michael,

Bene, puoi creare Worker, service worker e usare l'API canvas usando un url di dati. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'). È anche un buon modo per verificare se si dispone del supporto per le espressioni di importazione:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
Fábio Santos

9

La riproduzione di un suono ~ vuoto costringe il browser a conservare le prestazioni - l'ho scoperto dopo aver letto questo commento: Come far funzionare JavaScript a velocità normale in Chrome anche quando la scheda non è attiva?

Ho bisogno di prestazioni illimitate su richiesta per un browser game che utilizza WebSocket, quindi so per esperienza che l'uso di WebSocket non garantisce prestazioni illimitate, ma dai test, la riproduzione di un file audio sembra assicurarlo

Ecco 2 loop audio vuoti che ho creato a questo scopo, puoi usarli liberamente, commercialmente: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Includono rumore di -58db, -60db non funziona)

Li suono, su richiesta dell'utente, con 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,
    });
}

È triste che non esiste un metodo integrato per attivare / disattivare le prestazioni complete di JavaScript per impostazione predefinita, tuttavia, i minatori di crittografia possono dirottare tutti i thread di elaborazione utilizzando Web Worker senza alcun prompt: |


Grazie, 58db è molto sopportabile con le cuffie, ma il silenziamento del sito risolve quel problema
Kaan Soral,

1

Ho rilasciato il pacchetto npm a intervallo di lavoro che imposta l'implementazione Interval e clearInterval con l'utilizzo di Web-Workers per essere sempre aggiornato su schede inattive per Chrome, Firefox e IE.

La maggior parte dei browser moderni (Chrome, Firefox e IE), gli intervalli (timer della finestra) sono bloccati per sparare non più di una volta al secondo nelle schede inattive.

Puoi trovare maggiori informazioni su

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


0

Ho aggiornato il mio core jQuery alla 1.9.1 e ho risolto la discrepanza dell'intervallo nelle schede inattive. Vorrei provarlo prima, quindi cercare altre opzioni di sostituzione del codice.


da quale versione hai effettuato l'aggiornamento? Ho riscontrato alcuni problemi di timeout (cursori della galleria) con la versione ~ 1.6
dmi3y

0

ecco la mia soluzione che ottiene il millisecondo corrente e lo confronta con il millisecondo in cui è stata creata la funzione. per intervallo, aggiornerà il millisecondo quando esegue la funzione. puoi anche prendere l'intervallo / timeout di un ID.

<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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.