Chrome: limity czasu / interwały zawieszone na kartach w tle?

133

Testowałem dokładność setTimeoutużycia tego testu . Teraz zauważyłem, że (zgodnie z oczekiwaniami) setTimeoutnie jest zbyt dokładny, ale w przypadku większości urządzeń nie jest dramatycznie niedokładny. Teraz, jeśli uruchomię test w Chrome i pozwolę mu działać na karcie w tle (więc przełączam się na inną kartę i przeglądam tam), wracam do testu i sprawdzam wyniki (jeśli test się skończył), są dramatycznie zmieniane. Wygląda na to, że limity czasu są znacznie wolniejsze. Testowane w FF4 lub IE9 to nie wystąpiło.

Wygląda więc na to, że Chrome zawiesza lub przynajmniej spowalnia wykonywanie javascript na karcie, która nie jest aktywna. Nie udało się znaleźć w sieci zbyt wielu informacji na ten temat. Oznaczałoby to, że nie możemy uruchamiać zadań w tle, takich jak na przykład okresowe sprawdzanie na serwerze za pomocą wywołań XHR i setInterval(podejrzewam, że widzę to samo zachowanie setInterval, napiszę test, jeśli czas jest ze mną).

Czy ktoś to spotkał? Czy byłoby jakieś obejście tego zawieszenia / spowolnienia? Czy nazwałbyś to błędem i czy powinienem to zgłosić jako taki?

KooiInc
źródło
Ciekawy! Czy możesz stwierdzić, czy Chrome wstrzymuje i wznawia licznik czasu, czy też go restartuje, gdy ponownie uzyskasz dostęp do karty? A może zachowanie jest przypadkowe? Czy może to mieć coś wspólnego z faktem, że Chrome uruchamia karty w niezależnych procesach?
HajdarA
@gAMBOOKa: spójrz na odpowiedź @ pimvdb. Prawdopodobnie jest to spowolnienie maksymalnie do jednego razu na sekundę.
KooiInc
4 lata później ten problem nadal istnieje. Mam setTimeOut dla elementów div z a transition, więc nie wszystkie elementy div przechodzą w tym samym czasie, ale w rzeczywistości 15 ms po sobie, tworząc efekt toczenia. Kiedy przechodzę do innej karty i wracam po chwili, wszystkie elementy div zmieniają się w tym samym czasie i setTimeOutjest całkowicie ignorowany. Nie jest to duży problem dla mojego projektu, ale jest to dziwny i niechciany dodatek.
Rvervuurt
Dla naszej animacji, która wywołała setTimeout w sekwencji, rozwiązaniem dla nas było po prostu upewnienie się, że pamiętamy uchwyt / identyfikator timera (jest on zwracany z setTimeout) i zanim ustawimy nowy timer, najpierw wywołamy clearTimeout, jeśli mamy mam rączkę. W naszym przypadku oznacza to, że po powrocie do karty może wystąpić początkowa dziwność w zakresie odtwarzanej animacji, ale układa się ona dość szybko i normalna animacja zostaje wznowiona. Początkowo myśleliśmy, że to problem bez kodu.
Akcja Dan

Odpowiedzi:

92

Niedawno o to zapytałem i jest to zachowanie zgodne z projektem. Gdy karta jest nieaktywna, wywoływana jest funkcja maksymalnie raz na sekundę. Oto zmiana kodu .

Być może to pomoże: Jak sprawić, by setInterval działał również, gdy karta jest nieaktywna w Chrome?

TL; DR: użyj Web Workers .

pimvdb
źródło
3
dzięki, powinienem był wyglądać z „nieaktywną kartą”. Nie bycie rodzimym użytkownikiem języka angielskiego jest czasami utrudnieniem.
KooiInc
1
@Kooilnc: Żaden problem :) Ja też nie jestem native speakerem języka angielskiego.
pimvdb
24

Istnieje rozwiązanie do korzystania z Web Workerów, ponieważ działają one w oddzielnym procesie i nie są spowalniane

Napisałem mały skrypt, którego można używać bez zmian w kodzie - po prostu zastępuje funkcje setTimeout, clearTimeout, setInterval, clearInterval

Po prostu dołącz go przed całym kodem

http://github.com/turuslan/HackTimer

Rusłan Tuszow
źródło
7
To miłe, ale pamiętaj, że: 1. Pracownicy nie mają dostępu do DOM, 2. Pracownicy są zawsze wykonywane tylko wtedy, gdy znajdują się na własnym pliku. W wielu przypadkach nie jest to zastępczy zamiennik setTimeout.
Madara's Ghost
1
Masz rację, ale niektóre nowoczesne przeglądarki pozwalają na używanie pracowników bez ich własnych plików za pomocą Blobów ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
Ruslan Tushov
1
Mimo to, pracownikom sieciowym brakuje wielu funkcji (mianowicie DOM), które pozwalają im być bezpiecznym zamiennikiem setTimeout i co.
Madara's Ghost
A co z kodem, który musi działać z przodu, na przykład z ciężkimi zadaniami przetwarzania grafiki, które chcielibyśmy zakończyć, gdy robimy inne rzeczy?
Michael
Cóż, możesz tworzyć pracowników, pracowników usług i używać interfejsu API kanwy przy użyciu adresu URL danych. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'). Jest to również dobry sposób na sprawdzenie, czy masz obsługę wyrażeń importu:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
Fábio Santos
10

Odtwarzanie ~ pustego dźwięku zmusza przeglądarkę do zachowania wydajności - odkryłem to po przeczytaniu tego komentarza: Jak sprawić, by JavaScript działał z normalną prędkością w Chrome, nawet gdy karta nie jest aktywna?

Potrzebuję nieograniczonej wydajności na żądanie dla gry przeglądarkowej korzystającej z WebSockets, więc wiem z doświadczenia, że ​​korzystanie z WebSockets nie zapewnia nieograniczonej wydajności, ale z testów wydaje się, że odtwarzanie pliku audio to zapewnia

Oto 2 puste pętle audio, które utworzyłem w tym celu, możesz z nich swobodnie korzystać komercyjnie: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Obejmują hałas -58db, -60db nie działa)

Gram w nie na żądanie użytkownika z 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,
    });
}

To smutne, że nie ma wbudowanej metody domyślnie włączającej / wyłączającej pełną wydajność javascript, jednak kopacze kryptowalut mogą przejąć wszystkie wątki obliczeniowe przy użyciu narzędzi sieciowych bez żadnego monitu: |

Kaan Soral
źródło
Dziękuję, 58 dB jest bardzo dobrze słyszalne ze słuchawkami, ale wyciszenie strony rozwiązuje ten problem
Kaan Soral
2

Wydałem pakiet work -interval npm, który implementuje setInterval i clearInterval z wykorzystaniem Web-Workers do utrzymania i działania na nieaktywnych kartach dla Chrome, Firefox i IE.

W większości nowoczesnych przeglądarek (Chrome, Firefox i IE) interwały (liczniki czasu okien) są uruchamiane nie częściej niż raz na sekundę w nieaktywnych kartach.

Możesz znaleźć więcej informacji na temat

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
źródło
0

Zaktualizowałem mój rdzeń jQuery do wersji 1.9.1 i rozwiązałem problem rozbieżności interwałów w nieaktywnych zakładkach. Najpierw spróbuję tego, a potem przyjrzę się innym opcjom nadpisywania kodu.

Carey Estes
źródło
z której wersji dokonałeś aktualizacji?
Wystąpiły
0

oto moje rozwiązanie, które pobiera bieżącą milisekundę i porównuje ją z milisekundą, w której funkcja została utworzona. przez interwał zaktualizuje milisekundę, gdy uruchomi funkcję. możesz również pobrać interwał / limit czasu za pomocą identyfikatora.

<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
źródło