Pravidelné spouštění JavaScript kódu
Školení, která pořádám
Před časem jsem psal o tom, jak u klienta zobrazit serverový čas. Tuto problematiku rozebírá i Otec Fura a navíc uvádí problém spočívající v tom, že metoda setInterval nespouští událost v přesně daném intervalu, ale postupně se zpožďuje.
Jak tento problém řešit? Myšlenka spočívá v tom, že místo setInterval budeme volat setTimeout s proměnlivou odchylkou. Tu vypočteme jako rozdíl skutečného a požadovaného času spuštění. Pokud se tedy metoda setTimeout zpozdí např. o 50 ms, tak ji příště nezavoláme třeba za 1000 ms, ale za 950 ms.
<script type="text/javascript">
/** Volání funkce v pravidelných intervalech
* @param function volaná funkce
* @param number počet milisekund, po kterých se funkce spouští
* @return number identifikátor použitelný v metodě clearTimeout()
*/
function setRealInterval(func, delay, reqTime) {
var d = new Date();
reqTime = reqTime || d.getTime();
return setTimeout(function () {
func();
setRealInterval(func, delay, reqTime + delay);
}, delay + reqTime - d.getTime());
}
</script>
Funkce neřeší stav, kdy by odchylka byla větší než požadované zpoždění, takže může metodě setTimeout předat záporný parametr (podle hesla „včera bylo pozdě“).
Přijďte si o tomto tématu popovídat na školení JavaScript a AJAX.
Diskuse
Viz. reakce pod článkem na mém blogu:
Pochopil jsem co jste měl na mysli. Spíš na to reaguji ve smyslu - přestože v daném okamžiku víte, že máte zpoždění 50ms a načasujete tedy další timer na 950ms místo 1000s nikde nemáte zaručeno, že se vám timer skutečně zavolá za 950ms a ne třeba až za 1300ms. Podle mého názoru tím daný problém neřešíte.
naniccz:
Já tohle řeším tak, že pomocí PHP do JS vložím serverový čas, potom spočtu odchylku serveru od usera, a pak volám třeba po 900ms fci na reload času. Ta ale neinkrementuje sekundy, ale vezme klientský čas, přičte/odečte odchylku a zobrazí. (je to náchylné, pokud si user posune hodiny až po načtení stránky.... ale jak často se to stane, to spíš může mít vypnutý JS)
naniccz:
Což je popisováno v článku Honzy, a navazováno zde. Omlouvám se za objevení Ameriky
Jakub Vrána :
900 ms není dobrá volba. Pokud bude časovač fungovat správně, tak bude čas:
0,0 ~ 0
0,9 ~ 1
...
4,5 ~ 5
5,4 ~ 5
Takže už po 5 s se hodiny na chvíli jakoby zastaví. Problém je v tom, že se dopředu nedá odhadnout, jak bude zpoždění narůstat, proto je potřeba ho nastavovat dynamicky.
Michal Aichinger:
Myslím, že je takovýhle řešení blbost jako škrábání se levou za pravým uchem. Prostě řešit kumulativni odchylku takto nejde.
V případě animací se používá to, že se poznačí počátek spuštění a nastaví timer. Nicméně s jeho krokem se vůbec nepracuje, pracuje se s rozdílem počátečního a aktuálního času.
Jakub Vrána :
"Blbost" je to jistě v situaci, kdy by se to použilo u animace. Ale v článku je jasně napsáno, že se tento postup hodí pro zobrazení serverového času. A tam to dává velmi dobrý smysl, protože pokud chceme, aby nenarůstalo zpoždění a zároveň se hodiny aktualizovaly pravidelně, tak jediná alternativa k tomuto řešení je nastavit časovač třeba na 50 ms, což je už nepříjemně často.
Kajman:
A když je třeba to počítání zastavit (např. aktualizuji input s časem a chci ho po kliknutí editovat), tak musím použít nějakou globální proměnnou?
v6ak:
Všiml jsem si jedné chyby: navrácené id intervalu je k ničemu po prvním zavolání události. Pak se už používá jiné číslo.
Jako řešení zde IMHO stačí smazat return a @return s tím, že doplnění této funkcionality bude za domácí úkol. (Ale tabulka realIntervalId->timeoutId je taky řešení.)
Diskuse je zrušena z důvodu spamu.