Autor | Zpráva | ||
---|---|---|---|
gen001 Profil * |
#1 · Zasláno: 22. 6. 2015, 06:32:24
Ahoj,
procházel jsem novinky z es6 a narazil na generatory. jedna z hlavnich vyhod by mela byt ze narozdíl od abyčejné fce jsou neblokující. tady je ukázkový kód: setTimeout(function(){ console.log("Hello World"); },1); function foo() { // NOTE: don't ever do crazy long-running loops like this for (var i=0; i<=1E10; i++) { console.log(i); } } foo(); // 0..1E10 // "Hello World" jak ale zařídit aby se hello word nevypsalo az na konci? zkusil jsem neco jako nasledujici kod ale vysledek je bohuzel stejný (testováno v aktuální verzi chrome) var start = new Date().getTime(); setTimeout(function(){ var end = new Date().getTime(); var time = end - start; console.log('Execution time: ' + time); console.log("Hello World"); },1); function *foo(){ var n = 1; for (var i=0; i<=1E4; i++) { console.log(n) yield n++; } } for (var p of foo()) {} co dělám špatně? předem děkuji. |
||
MartinP Profil * |
#2 · Zasláno: 22. 6. 2015, 08:23:57
Neni
for (...) {} blokující? Zkus místo toho jen zavolat tu fci.
|
||
Chamurappi Profil |
#3 · Zasláno: 22. 6. 2015, 08:24:22
Reaguji na gena001:
„co dělám špatně?“ Už je to aspoň šest let, co jsem generátory v JS zkoumal, tak se možná pletu, ale podle mě vycházíš z mylných předpokladů. Generátor je jen jiný pohled na iterování umožňující hladší provázání kroků. Neblokují se navzájem provázané generátorové funkce (tzn. nečeká se na dokončení jedné, aby začala s polo-výsledkem pracovat druhá), což ovšem nemá vůbec žádný vliv na způsob zpracování událostí. Pořád máme jedno vlákno. Novinka v ES6 je jen syntaktický cukr (zpětně nekompatibilní!) pro něco, co šlo odjakživa napodobit. Na vícevláknové úkony vznikly Web Workers. |
||
gen001 Profil * |
#4 · Zasláno: 22. 6. 2015, 09:18:18 · Upravil/a: gen001
Chamurappi:
vycházel jsem z tohoto článku: http://davidwalsh.name/es6-generators kde hned na začátku je příklad co jsem uvedl nahoře. možná tedy chybně jsem předpokládal že generátory mohou daný problém vyřešit. přímo tam píší With ES6 generators, we have a different kind of function, which may be paused in the middle, one or many times, and resumed later, allowing other code to run during these paused periods. MartinP: zkusil jsem to předělat na něco jako function *foo(){ var n = 1; while (true) { console.log(n) yield n++; } } var gen = foo() gen.next() gen.next() 10000x gen.next() a vysledek je stejný. přijde mi, že po vykonání prvního gen.next() sice vznikne ona pauza na zavolání dalšího kódu ale první co se zavolá je další gen.next() a tak dále takže samotný callback z timeout se zavolá až po posledním gen.next(). |
||
Radek9 Profil |
gen001:
„přijde mi, že po vykonání prvního gen.next() sice vznikne ona pauza na zavolání dalšího kódu ale první co se zavolá je další gen.next()“ To se děje proto, že přesně takhle jsi ten kód napsal. Jde o to, že můžeš zavolat next , potom tam nechat pauzu a později (tedy logicky asynchronně, nebo po nějaké sekvenci jiných příkazů) zavolat další next , přičemž generátor běží od toho místa, kde původně přestal. Můžeš to tedy kdykoli přerušit a potom obnovit ve chvíli, kterou ty uznáš za vhodné. To je ta výhoda.
|
||
Jan Tvrdík Profil |
#6 · Zasláno: 22. 6. 2015, 11:11:23
gen001:
„možná tedy chybně jsem předpokládal že generátory mohou daný problém vyřešit“ Generátory ti umožní relativně snadno dělat to samé, co dělá každý procesor v PC – přepínat mezi jednotlivými úlohami, čímž vytvoříš iluzi paralelní běhu. Viz také výborný článek o yieldu v PHP. |
||
gen001 Profil * |
#7 · Zasláno: 22. 6. 2015, 12:21:29
Radek9:
jistě, ale předpokládám že mezi zavoláním x-teho next() by měla být možnost, aby se vyhodnotil timeout který mezitím končí a vypsal se tedy někde uprostřed. nikoli až na konci. |
||
Jan Tvrdík Profil |
#8 · Zasláno: 22. 6. 2015, 12:31:13
gen001:
Ano, ale musíš si ručně napsat ten scheduler (česky asi plánovač procesů). Nefunguje to tak, že po zavolání yield budou automaticky zavolány úlohy, které čekají ve frontě (např. protože doběhl timeout).
|
||
Radek9 Profil |
#9 · Zasláno: 22. 6. 2015, 13:09:08
gen001:
„předpokládám že mezi zavoláním x-teho next() by měla být možnost, aby se vyhodnotil timeout“ Předpokládáš špatně. V tu chvíli ještě není uvolněn stack na hlavním vlákně, takže se ta událost z timeoutu řadí až za tu sekvenci volání next (nebo případně ještě za další kód, který následuje). Ty generátory opravdu nejsou žádné magické vylepšováky, blokují vlákno stejně jako cokoli jiného.
|
||
gen001 Profil * |
#10 · Zasláno: 22. 6. 2015, 17:30:34
ok, díky za info. pak vážně nechápu proč někdo v článku uvádí příklad na něčem co stejně nelze pomocí generátorů napsat lépe...
|
||
Jan Tvrdík Profil |
#11 · Zasláno: 22. 6. 2015, 17:45:50
gen001:
Ale pomocí generátorů to jde napsat „lépe“, tedy že jedna operace nemusí čekat na dokončení druhé. Pochopil jsi aspoň něco z toho, co jsem výše psal? |
||
gen001 Profil * |
#12 · Zasláno: 23. 6. 2015, 20:17:26
Jan Tvrdík:
ale samotný callback setTimeout bude stejně až nakonec. což mi připadalo jako hlavní bod k řešení. a ten zůstává nevyřešen... |
||
Jan Tvrdík Profil |
#13 · Zasláno: 23. 6. 2015, 21:54:14
gen001:
Ne nutně. Buď si můžeš napsat vlastní implementaci setTimeout , kdy ty callbacky bude volat scheduler nebo scheduler může uvolnit hlavní vlákno a přidat pomocí setTimeout s nulovým zpožděním pokračování sebe sama nebo scheduler může uvolnit hlavní vlákno a callback uvnitř setTimeout zavolá po svém dokončením explicitně scheduler aby pokračoval.
Nicméně všechno tohle jsou pro normální weby zbytečně komplikovaná řešení, nevím o tom, že by to tak někdo někdy používal, už proto, že generátory jsou podporované až v ES6, takže je musíš v praxi kompilovat do ES5. |
||
Chamurappi Profil |
#14 · Zasláno: 23. 6. 2015, 22:05:31
Reaguji na gena001:
„a ten zůstává nevyřešen...“ Nevyřešen na úrovni ECMAScriptu. Na něm ani není, aby tohle řešil, logicky to do něj nepasuje. Metoda setTimeout je součástí DOMu, nastavuje časovanou událost, patří k API prohlížeče, nemůže změnit charakter jazyka. Od onclick u bys také neočekával, že může nastat uprostřed běhu jiné JS funkce.
|
||
_es Profil |
[#13] Jan Tvrdík:
Asi tomu nerozumiem. Čo myslíš po „uvoľnením hlavného vlákna“? Všetok JS beží v jednom vlákne a keď funkcia beží, tak nie je žiadny spôsob, ako ju z JS ukončiť - musí skončiť sama od seba, či ju ukončí niečo „nadradenejšie“ - napríklad zavretie prehliadača či niektoré prehliadače ponúknu ju zavrieť ako pridlho bežúci skript a pod. Nie je nič také ako možnosť spustiť funkciu na trebárs maximálne 1 s, potom spustiť inú funkciu, po jej dokončení prvú dokončiť a pod. Takže mi nie je jasné, ako by ten „scheduler“ mal fungovať. |
||
Jan Tvrdík Profil |
_es:
„Takže mi nie je jasné, ako by ten ‚scheduler‘ mal fungovať.“ Četl jsi výše odkázaný článek nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html? Pokud si představíš ten scheduler jako jeden velký while(true) cyklus, tak uvnitř může být něco jako:
if (step % 100 == 0) { setTimeout(() => continueWith(state), 0); // zařadí nakonec fronty pokračování break; // uvolní hlavní vlákno } Taky je možný, že jsem něco nedomyslel =) |
||
_es Profil |
#17 · Zasláno: 24. 6. 2015, 15:53:29
Jan Tvrdík:
Článok sa predsa týka PHP, nie je tam nič o JavaScripte. Ak sa v JS spraví nejaký „velký while(true) cyklus“ tak to akurát zablokuje celú stránku. Použitie setTimeout s nulovým oneskorením namiesto priameho zavolania funkcie spôsobí len to, že sa funkcia zavolá až za všetkými dokončenými príkazmi za setTimeout . Žiadny „multitasking“ sa s tým nevyrobí.
|
||
Jan Tvrdík Profil |
_es:
„Článok sa predsa týka PHP, nie je tam nič o JavaScripte.“ Všechno co je v tom článku je IMHO aplikovatelné na JS. PHP má taky jedno vlákno. Syntaxe je skoro stejná. „Ak sa v JS spraví nejaký ‚velký while(true) cyklus‘ tak to akurát zablokuje celú stránku.“ Nesmysl. Záleží přece na obsahu toho cyklu. „spôsobí len to, že sa funkcia zavolá až za všetkými dokončenými príkazmi za setTimeout .“
Což bylo přesně cílem. Dočasně uvolní vlákno, aby mohl proběhnout hotový setTimeout a zase pokračuje.
„Žiadny ‚multitasking‘ sa s tým nevyrobí.“ Záleží na definici multitaskingu. Celou dobu tvrdím, že jde o stejnou emulaci, kterou dělá normální procesor. Multitasking v počítači nebo v telefonu taky funguje i na jednom jádře. |
||
Radek9 Profil |
#19 · Zasláno: 24. 6. 2015, 18:58:02
Jan Tvrdík:
„PHP má taky jedno vlákno. Syntaxe je skoro stejná.“ Jenže v PHP ti je jedno, že to vlákno blokneš, protože ho na nic jiného nepotřebuješ. „Nesmysl. Záleží přece na obsahu toho cyklu.“ Zkusils to? Jakýkoli uživatelský vstup je v tu chvíli znemožněn, ať ten while obsahuje cokoli. Aby to fungovalo zhruba tak, jak ty říkáš, a přitom to nic neblokovalo, tak bys musel každou tu sekvenci příkazů řadit na konec stacku (použitím setTimeout nebo setImmediate ). V případě toho generatoru nějak takhle:
setImmediate(function run() { if (!it.next().done) { setImmediate(run); } }); |
||
Jan Tvrdík Profil |
#20 · Zasláno: 24. 6. 2015, 19:10:25
Ach, já to asi nevysvětlím. Tak tady máte celé funkční řešení toho původního problému [#1] pomocí generátorů:
setTimeout(() => console.log("Hello World"), 20); // jobs function* foo() { for (var i = 0; i < 1000; i++) { console.log("A%d", i); yield; } } function* bar() { for (var i = 0; i < 700; i++) { console.log("B%d", i); yield; } } // scheduler function run(i, jobs) { while (jobs.length) { var j = (i++) % jobs.length; if (jobs[j].next().done) { jobs.splice(j, 1); } if (i % 5 === 0) { setTimeout(() => run(i, jobs), 0); break; } } } run(0, [foo(), bar()]); |
||
_es Profil |
#21 · Zasláno: 24. 6. 2015, 21:51:54
Jan Tvrdík:
„Záleží na definici multitaskingu.“ Obvykle má ovládanie multitaskingu možnosť prerušiť beh pridlho trvajúcej úlohy a spustiť iné. To v JS nie je možné. |
||
gen001 Profil * |
#22 · Zasláno: 25. 6. 2015, 11:57:39
Jan Tvrdík:
vazne diky moc za ukazku |
||
Časová prodleva: 9 let
|
0