Chrome: tempos limite / intervalo suspensos nas guias em segundo plano?


130

Eu estava testando a precisão do setTimeoutuso deste teste . Agora notei que (como esperado) setTimeoutnão é muito preciso, mas para a maioria dos aparelhos não é dramaticamente impreciso. Agora, se eu executar o teste no Chrome e deixá-lo em uma guia em segundo plano (alternando para outra guia e navegando lá), retornando ao teste e inspecionando os resultados (se o teste terminar), eles serão alterados drasticamente. Parece que os tempos limite estiveram muito mais lentos. Testado em FF4 ou IE9, isso não ocorreu.

Parece que o Chrome suspende ou pelo menos diminui a execução do javascript em uma guia que não tem foco. Não foi possível encontrar muito na rede sobre o assunto. Isso significaria que não podemos executar tarefas em segundo plano, como, por exemplo, verificar periodicamente em um servidor usando chamadas XHR e setInterval(eu suspeito de ver o mesmo comportamento setInterval, escreveremos um teste se houver tempo comigo).

Alguém já encontrou isso? Haveria uma solução alternativa para essa suspensão / desaceleração? Você chamaria isso de bug e devo arquivá-lo como tal?


Interessante! Você pode dizer se o Chrome está pausando e retomando o cronômetro ou reiniciando-o depois de acessar novamente a guia? Ou o comportamento é aleatório? Poderia ter algo a ver com o fato de o Chrome executar guias em processos independentes?
— HyderA

@gAMBOOKa: dê uma olhada na resposta de @ pimvdb. É provável que ocorra uma desaceleração no máximo uma vez por segundo.
— KooiInc

4 anos depois e esse problema ainda existe. Eu tenho um setTimeOut para divs com a transition, portanto nem todos os divs fazem a transição ao mesmo tempo, mas na verdade 15ms um após o outro, criando algum efeito contínuo. Quando vou para outra guia e volto depois de um tempo, todos os divs são transferidos ao mesmo tempo e o setTimeOuté completamente ignorado. Não é um grande problema para o meu projeto, mas é uma adição estranha e indesejada.
— Rvervuurt #

Para nossa animação que chamou setTimeout em uma sequência, a solução para nós era apenas garantir que lembrássemos o identificador / ID do timer (retornado de setTimeout) e, antes de definir um novo timer, chamaremos clearTimeout se tivermos peguei a manivela. No nosso caso, isso significa que, quando você retorna à guia, pode haver alguma estranheza inicial em termos de qual animação está sendo reproduzida, mas ela se resolve rapidamente e a animação regular é retomada. Nós pensamos que este era um problema com o código inicialmente.
— Ação Dan

Respostas:


88

Recentemente, perguntei sobre isso e é comportamento por design. Quando uma guia está inativa, somente no máximo uma vez por segundo a função é chamada. Aqui está a alteração do código .

Talvez isso ajude: Como posso fazer o setInterval também funcionar quando uma guia está inativa no Chrome?

TL; DR: use Web Workers .


3
obrigado, eu deveria ter olhado com 'guia inativo'. Não ser um falante nativo de inglês às vezes é uma desvantagem.
— KooiInc

1
@Kooilnc: Não tem problema :) Também não sou um falante nativo de inglês.
— Pimvdb 17/05

22

Existe uma solução para usar Web Workers, porque eles são executados em processo separado e não são mais lentos

Eu escrevi um pequeno script que pode ser usado sem alterações no seu código - ele simplesmente substitui as funções setTimeout, clearTimeout, setInterval, clearInterval

Basta incluí-lo antes de todo o seu código

http://github.com/turuslan/HackTimer


7
Isso é legal e esteja ciente de que: 1. Os trabalhadores não têm acesso ao DOM, 2. Os trabalhadores só são executados se estiverem em um arquivo por conta própria. É não um substituto para setTimeout para um monte de casos.
— Ghost de Madara

1
Você está certo, mas alguns navegadores modernos permitem utilizar trabalhadores sem os seus próprios arquivos usando Blobs ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
— Ruslan Tushov

1
Mesmo com isso, os Web Workers estão perdendo muitas funcionalidades (a saber, DOM) que lhes permitem ser um substituto seguro para setTimeout e co.
— Fantasma de Madara

E o código que precisa ser executado no front end, por exemplo, tarefas pesadas de processamento de gráficos que gostaríamos de concluir enquanto fazemos outras coisas?
— Michael

Bem, você pode criar Trabalhadores, prestadores de serviços e usar a API do canvas usando um URL de dados. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'). Também é uma boa maneira de verificar se você possui suporte à expressão de importação:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
— Fábio Santos

9

Tocar um som ~ vazio força o navegador a manter o desempenho - eu o descobri depois de ler este comentário: Como fazer o JavaScript ser executado na velocidade normal no Chrome, mesmo quando a guia não está ativa?

Preciso de desempenho ilimitado sob demanda para um jogo de navegador que usa o WebSockets, por isso sei por experiência própria que o uso do WebSockets não garante desempenho ilimitado, mas a partir de testes, a reprodução de um arquivo de áudio parece garantir

Aqui estão dois loops de áudio vazios que criei para esse fim. Você pode usá-los livremente, comercialmente: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Eles incluem ruído de -58db, -60db não funciona)

Eu os reproduzo, sob demanda do usuário, com o 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 que não exista um método interno para ativar / desativar o desempenho completo do javascript por padrão; no entanto, os mineradores de criptografia podem seqüestrar todos os seus threads de computação usando Web Workers sem nenhum aviso: |


Obrigado, 58dB é muito audível com fones de ouvido tho, mas silenciando os resolve local esse problema
— Kaan Soral

1

Eu liberei trabalhador de intervalo de pacote npm que setInterval e clearInterval implementação com o uso da Web de trabalho para manter em funcionamento em abas inativas para o Chrome, Firefox e IE.

A maioria dos navegadores modernos (Chrome, Firefox e IE), intervalos (temporizadores de janelas) são fixados para disparar não mais que uma vez por segundo em guias inativas.

Você pode encontrar mais informações sobre

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

Atualizei meu núcleo do jQuery para a versão 1.9.1 e resolveu a discrepância do intervalo nas guias inativas. Eu tentaria isso primeiro e depois procuraria outras opções de substituição de código.


de qual versão você atualizou? Eu experimentei alguns problemas de tempo limite (Galeria sliders) com versão ~ 1,6
— dmi3y

0

aqui está minha solução que obtém o milissegundo atual e a compara com o milissegundo em que a função foi criada. por intervalo, ele atualizará o milissegundo quando executar a função. você também pode pegar o intervalo / tempo limite por um 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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.