Funkce window.setTimeout v PHP

Školení, která pořádám

Jeden můj kamarád dostal při pohovoru (nikoliv do Facebooku) zajímavou otázku: „Naprogramuj JavaScriptovou funkci setTimeout v PHP.“ Jak jsem to slyšel, začala mi hlava šrotovat, vymýšlet řešení a narážet na první komplikace ještě dřív, než jsem vzal do ruky tužku nebo klávesnici. A i když jsem si nejdřív říkal, že je to úloha zaměřená hlavně na technické detaily implementace místo na algoritmizaci (o což by mělo jít víc), tak se v ní algoritmus přeci jen použije. Tím pádem se dá rozebrat jeho časová složitost, která je při přímočaré implementaci špatná, ale dá se vylepšit. Prostě mi z toho nakonec vychází perfektní úloha k pohovoru.

Pokud vás úloha taky zaujala, zkuste si ji nejprve vyřešit sami:

Nevíte si s něčím rady? Nejste si jisti, jestli jste na něco nezapomněli? Podívejte se na nápovědu. Časem se dostaneme i k mému řešení.

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

Diskuse

mp:

zaujimave je ze v google reader je cely clanok :)

mp:

joj slepy som... je aj tu :)

ikona David Grudl OpenID:

Použil bych na to jednu funkci a speciální konstrukci, která tam existuje od doby, co PHP znám, ale nikdy jsem ji nepoužil. Ona konstrukce je mimochodem zmíněná ve všech knihách o PHP 6 :-)

ikona Jakub Vrána OpenID:

U pohovoru by mi to asi vadilo. Jednak považuji za zásadní vlastnost setTimeout(), že čeká na doběhnutí spuštěného kódu, což způsob jeho využití zásadně ovlivňuje (zjednodušuje). A jednak by tohle řešení (na rozdíl od toho druhého) bylo nepoužitelně pomalé.

ikona David Grudl OpenID:

ad předávání referencí: mělo by jít register_shutdown_function(function () use (& $timeouts) {});

ikona Jakub Vrána OpenID:

Fakt to jde, díky. Doplnil jsem to do článku.

ikona David Grudl OpenID:

a do třetice: to rozbalování je vážně cool!

ikona Jakub Vrána OpenID:

Hlavně když ho musíš dělat po odeslání každého příspěvku do diskuse znovu, co?

paranoiq:

příklad s `declare(ticks = 1)` nebude fungovat. důvodem je to, že ticky nejsou vykonávány v runtimu. namísto toho je volání ticků vloženo kompilátorem přímo do kódu nad kterým jsou deklarovány. opustí-li tedy běh kódu oblast pro kterou jsou ticky deklarovány, přestanou se ticky provádět

ikona Jakub Vrána OpenID:

Zajímavé, to mě nenapadlo. Takže bychom museli declare(ticks = 1) napsat do všech vložených souborů.

Ugo:

hezký, na tom bych solidně pohořel (co kamarád?). Ale i když jsem to nezkoušel, tak mě napadá, že dávat timeout až na konec nemusí být vždy funkční ne? Když mi script běží 10 vteřin a na začátku dám aby se něco zavolalo po 5 vteřinách, tak předpokládám, že mám asi smůlu, ale jak to vyřešit, když zavolám jiný script nebo přímo php v konzoli, tak zrovna tak se bude čekat na dokončení. btw. co je konstrukce zmiňovaná v knihách o PHP 6? jestli někdo zná opravdu funkční řešení, tak já bych se na něj ze zvědavosti rád podíval.

Ugo:

omlouvám se, toho rozbalování je tolik že jsem to nerozbalil celé :-P jdu si hrát s tikánim

Honza Prachař:

Mně funguje tohle (ale je tam ten problém, co zmiňuje paranoiq).

function setTimeout($c,$d){$t=microtime(1);register_tick_function(function()use(&$c,$d,$t){!$c||1e3*(microtime(1)-$t)>$d?:$c()&$c=0;});}

ikona Jakub Vrána OpenID:

Parádní Twitterové řešení. Ale třeba následující kód mi vypíše „ab“, i když by měl vypsat „ba“.

<?php
setTimeout
(function () {
    echo "a\n";
},
500);
setTimeout(function () {
    echo "b\n";
},
100);
sleep(1);
;
?>

Navíc to bude ještě pomalejší, protože microtime() se volá pro každou zaregistrovanou funkci zvlášť. Ale o to v tomhle případě zase tolik nejde.

Honza Prachař:

Jasně to bude tím, že "tick" se uděje až po uběhnutí té 1 s, a ty funkce se tím pádem zavolají v tom pořadí, v jakém byly zaregistrovány.

Richard Hutta:

Možná by stálo za to,

<?php
echo "You have to wait 10 seconds to see this.";
register_shutdown_function('shutdown');
exit;
function
shutdown(){
  sleep(10);
  echo "anything";
}

Richard Hutta:

Ou, sorry nevšiml jsem si té nápovědy :X

Standa:

K té složitosti - udržovat setříděné pole je zbytečné. Stačí halda, která se dá na začátku elegantně vybudovat v O(N). REMOVE_MIN a INSERT je pak stejně O(log N), ale aspoň je lepší konstanta.
Stejně si neumím představit udržování setříděného pole v O(log N). To spíš nějaký vyvážený strom, ale jak říkám, setříděnou posloupnost nepotřebujeme.

ikona Jakub Vrána OpenID:

Díky, kolego!

ikona Michal Gebauer:

Fce. usleep narozdíl od sleep nemůže být přerušena před zadaným časem?
Pokud může být přerušena myslím, že continue po usleep by ošetřilo tento případ.

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: Michal Gebauer

Franta:

A využití v praxi?

ikona Jakub Vrána OpenID:

Já bych si ho docela dovedl představit u nějakého démona – normálně dělá, co potřebuje, a jednou za čas se podívá, jestli není na serveru něco nového.

Franta:

A bude schopný po provedení té akce „jednou za čas“ pokračovat zase v tom, co „normálně dělá“?

Tony:

Mně to přijde jako dost nevhodný příklad na pohovor, právě proto, že k vyřešení je zapotřebí znát funkce, které se,jak jsi sám řekl, vůbec nepoužívají (declare ticks = 1), nebo zneužívat věci, které jsou k úplně jiným účelům (register_shutdown_function).

Přijde mi to jako úkol typu "GOTCHA" nebo "AHA, NO JO" - tj. úkol, jehož řešení už znám a pak vypadám jako borec, ale když ho neznám, tak se to bez nápovědy vyřešit nedá. Nepřijde mi to, jako úloha k pohovoru, kde by člověk měl prozkoumat kandidátovo logické myšlení, namísto znalosti obskurních detailů.

Franta:

+1, navíc docela pochybuji o tom praktickém využití (viz výše)

ikona Jakub Vrána OpenID:

„Úkol, jehož řešení neznám, se bez nápovědy vyřešit nedá.“

To je nesmysl. Já řešení neznal a úkol jsem bez nápovědy vyřešil.

Jinak register_shutdown_function() a především způsob odloženého provádění kódu v JS považuji za obvyklou znalost „seniornějších“ vývojářů.

Tomáš Ludvik:

v PHP 5.3 jsou ticky deprecated a v PHP 6 budou odebrány, viz. http://www.tuxradar.com/practicalphp/4/21/0
avatar © 2005-2020 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.