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.
Mám ale jistotu, že mi zpoždění nebude narůstat.
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)
Což je popisováno v článku Honzy, a navazováno zde. Omlouvám se za objevení Ameriky
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.
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.
"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?
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í.)