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.

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

Vložit komentář

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:

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.