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.

Jakub Vrána, Řešení problému, 25.2.2009, diskuse: 9 (nové: 0)

Diskuse

Honza Novotný:

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.

ikona Jakub Vrána OpenID:

Mám ale jistotu, že mi zpoždění nebude narůstat.

ikona 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)

ikona naniccz:

Což je popisováno v článku Honzy, a navazováno zde. Omlouvám se za objevení Ameriky

ikona Jakub Vrána OpenID:

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.

ikona 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.

ikona Jakub Vrána OpenID:

"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?

ikona 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.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.