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)

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL: Reakce na: naniccz

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í.)
avatar © 2005-2018 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.