Funkce window.setTimeout v PHP

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, on-line

Diskuse

mp:

zaujimave je ze v google reader je cely clanok :)
11.4.2012 01:44:58

mp:

joj slepy som... je aj tu :)
11.4.2012 02:00:17

ikona David Grudl:

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 :-)
11.4.2012 02:13:50

ikona Jakub Vrána:

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é.
11.4.2012 16:31:36

ikona David Grudl:

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

ikona Jakub Vrána:

Fakt to jde, díky. Doplnil jsem to do článku.
11.4.2012 03:41:55

ikona David Grudl:

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

ikona Jakub Vrána:

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

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
11.4.2012 10:29:08

ikona Jakub Vrána:

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

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.
11.4.2012 11:16:24

Ugo:

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

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;});}
11.4.2012 11:20:49

ikona Jakub Vrána:

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.
11.4.2012 16:26:08

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.
12.4.2012 10:24:34

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";
}
15.4.2012 19:33:53

Richard Hutta:

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

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.
19.4.2012 07:10:49

ikona Jakub Vrána:

Díky, kolego!
22.4.2012 09:53:03

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.
19.4.2012 15:34:34

Franta:

A využití v praxi?
20.4.2012 11:21:38

ikona Jakub Vrána:

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.
22.4.2012 09:43:26

Franta:

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

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

1.5.2012 15:15:02

Franta:

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

ikona Jakub Vrána:

„Ú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ářů.
2.5.2012 07:08:45

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
1.7.2014 20:34:08
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.