Dart – Futures

Webový vývojár dennodenne pracuje s ajaxovými requestami, callbackmi, obsluhovaním eventov. Neblokujúce API je v Javascripte štandardom, no nie je vždy úplne najpohodlnejšie používať ho. Dart rieši asynchrónne operácie pomocou Futures.
Seriál: Úvod do Dartu (9 dílů)
- Dart – Čo? Prečo? 2. 8. 2013
- Dart – Úvod do jazyka 23. 8. 2013
- Dart – Ponorme sa hlbšie 6. 9. 2013
- Dart – v DOMe 19. 9. 2013
- Dart – Futures 4. 10. 2013
- Dart – Streams 17. 10. 2013
- Dart – Používame JavaScript 1. 11. 2013
- Dart Typesystem 19. 11. 2013
- Dart – Neznesiteľná ľahkosť asynchrónneho bytia 2. 12. 2013
Nálepky:
Predstavme si, že máme sadu asynchrónnych operácií, ktoré potrebujeme vykonať v presne danom poradí. Typické riešenie pomocou callbackov by vyzeralo nasledovne:
void main() {
ajaxA(onSuccess: (resultA) {
ajaxB(onSuccess: (resultB) {
ajaxC(onSuccess: (resultC) {
handleC(resultC);
}, onError: () => handleError());
}, onError: () => handleError());
}, onError: () => handleError());
}
Takýto kód nie je úplne prehľadný.
Použime Futures!
V Darte asynchrónne operácie neočakávajú callback, miesto toho vracajú objekt typu Future
, tento objekt slúži ako akási reprezentácia hodnoty spočítanej v budúcnosti. Na Future
vieme pomocou metódy then
navesiť callback, ktorý sa zavolá v momente, ako bude aktuálna hodnota k dispozícii.
void main() {
var future = ajax();
var futureAlert = future.then((result) => "Success is performed by $result"));
futureAlert.then((value) => alert(value));
}
Vo vyššie uvedenom príklade sme do premennej future
priradili výsledok asynchrónnej operácie ajax()
. Následne sme cez future.then
navesili callback, ktorý vezme spočítanú hodnotu result
a vráti ju vo forme stringu (použité string interpolation). Všetko vrátené z callbacku vnútri then handlera sa považuje za ďalšiu Future
, v našom prípade futureAlert
. Na futureAlert
navesíme jednoduchý then
handler, ktorý alertne hodnotu.
Pre prípad, že nastane počas vyhodnocovania Future
nejaká chyba, môžeme ju zachytiť pomocou catchError
. Ak chceme zaregistrovať funkciu, ktorá sa vykoná v prípade chyby, no aj v prípade úspechu, môžeme použiť whenComplete
.
void main() {
var future = ajax();
future.then((value) => handleValue(value))
.catchError((error) => handleError()))
.whenComplete(() => alert("This is like finally!");
}
Použitie pripomína klasický try
–catch
–finally
blok a tak ho treba aj chápať – ako spôsob zápisu asynchrónneho obsluhovania výnimiek.
Poďme sa teraz pozrieť, ako pomocou Future
s môžeme zjednodušiť náš pôvodný kód.
void main() {
ajaxA().then((resultA) => ajaxB())
.then((resultB) => ajaxC())
.then((resultC) => handleC(resultC))
.catchError((error) => handleError())
Pri pozornejšom zamyslení sa nie je úplne jasné, prečo by mal uvedený kód fungovať tak, ako by sme chceli. Volanie ajaxB() nevracia hodnotu, ale Future a podla toho, čo sme si povedali, by táto Future mala byť obalená v ďalšej Future, ktorú vráti volanie then.
Toto je našťastie vyriešené šikovne. Metóda Future then(onValue(T value))
po zavolaní vráti Future f
, ktorá bude ukončená s výsledkom volania callbacku onValue
. V prípade, že onValue vráti Future f2
, f
je zreťazená s f2
, čo znamená, že f
je ukončená až v momente, kedy je ukončená f2
a to s rovnakým výsledkom ako f2
. Toto nám umožňuje reťaziť Futures
tak, ako v príklade.
Na poradí nezáleží.
Predstavme si inú situáciu. Náš kód potrebuje vykonať viacero asynchrónnych volaní, ktoré sú na sebe úplne nezávislé, no nemôžeme pokračovať skôr, ako budú dokončené. Ako efektívne vyriešiť toto?
void main() {
Future.wait([ajaxA(), ajaxB(), ajaxC()])
.then((values) => alert("A: ${values[0]}, B: ${values[1]}, C: ${values[2]}"))
.catchError((error) => handleError());
}
Statická metóda Future.wait
očakáva List futures
a výsledkom je Future
, ktorá je ukončená v momente, kedy je ukončený posledný prvok vo futures
. Výsledkom je List
hodnôt s ktorými jednotlivé prvky futures
skončili.
Chceme vlastné.
Naučili sme sa používať Future
s, vyrábať ich je ešte jednoduchšie. Stačí využiť služby predpripravenej triedy Completer
.
Future ajaxA() {
var completer = new Completer();
doSomeAjaxRequest(onSuccess: (result) {
completer.complete(result);
}, onError: () {
completer.completeError(new Exception("Error!"))
});
return completer.future;
}
Completer
vlastní property future
, ktorú z funkcie ajaxA
vraciame. V momente, kedy doSomeAjaxRequest
(ne)úspešne dobehne, ukončí Completer
future
(ne)úspešne pomocou metódy complete
, respektíve completeError
.
Feedback prosím!
Nájdite si prosím chvíľu času na ohodnotenie tohto článku.
Zdroje
Pri písaní článku som čerpal z viacerých hodnotných zdrojov, nižšie ich nájdete zoradené podľa užitočnosti, zaujímavosti a aktuálnosti.
- https://www.dartlang.org/articles/futures-and-error-handling/
- https://www.dartlang.org/articles/using-future-based-apis/
- http://api.dartlang.org/docs/releases/latest/dart_async/Completer.html
- http://api.dartlang.org/docs/releases/latest/dart_async/Future.html
- http://blog.sethladd.com/2012/03/using-futures-in-dart-for-better-async.html
Nelíbí se mi, že již v pátém díle seriálu je rozebrán obvyklý, ale krkolomný a zákeřný problém pro nováčky, aniž by čtenářům byly komplexně představeny základní principy jazyka. Byť jsou celkem podobné s klasickou céčkovou syntaxí, musí mít také nějaká specifika, přece to není jenom kompilátor C do JavaScriptu. Víte, jak to myslím, stačí takovéty klasické podmínky, hlášky, události, práce s html objekty, větvení, časování, apod. Sice v úvodu do seriálu bylo něco málo naznačeno ohledně funkcí a proměnných a postupně jsme při vytváření hry, přemýšlení o DOMu a zkoumání AJAXových struktur prošli mnoho příkladů a ukázek kódu a napsali několik aplikací, ale to nás nenaučilo řešit vlastní problém. Jakožto člověk, který se o to zajímá a se okolo webů pohybuje už nějakou dobu, jsem samozřejmě hned po 2. článku listoval dokumentací Dartu. Ale pro nováčky to může být problém. Navíc jste první, kdo Dart v Čechách podrobně rozpracovává a protože je to docela přívětivý jazyk i k nováčkům, stálo by za to také je trochu uvést do problematiky. Osobně mě tento poslední článek ničím už nezaujal a zdá se, že teď už budu muset spoléhat jen a jen na dokumentaci v angličtině, protože po dnešním článku si nejsem jist, budou-li mi další díly vůbec k něčemu, byť jsem úvod do jazyka doslova hltal. První články jsem si pročítal znovu a znovu, ale základní syntaxi a právě ta zajímavá pravidla větvení jsem vyčetl opravdu pouze z ukázek, již ne však z textu. Praxí a ukázkami je to parádní seriál na nové téma, ale zapomenulo se vlastně úplně na trochu té důležité teorie, což je podle mě škoda.
Moje hodnocení článků (jako ve škole):
1. díl: 1
2. díl: 1
3. díl: 3
4. díl: 3
5. díl: 4
Jak já to vidím, seriál vás dost oslovil, inspiroval a skvěle navnadil, takže jste se chopil iniciativy a vrhl se do problematiky. To je skvělé! Cíl dosažen. Mise splněna. Co mi uniká? 8-)
Já ano, jen si myslím, že někdo méně zkušený již ne…
Podla mna tiez nema zmysel roztahovat serialy na 3 nasobnu dlzku tym, ze sa bude ucit zakladna syntax. Ovela efektivnejsie mi pride, ked sa vysvetluju hlavne specifika a celkova filozofia vyvoja.
Mimochodom, tym, ktori chcu rychlo vstrebat zakladnu syntax Dartu odporucam tieto videa: https://www.dartlang.org/dart-tips/ . Je to pozeranie asi na hodinu a dobry doplnok k temam tohto serialu.
tie futures su tiez dost neprehladne, prirodzenejsi mi pride C#kovy await
//C#
var result = await webClient.DownloadStringAsync(„http://REFRESHER.SK/“);
alebo F# – kove asynchronne workflows:
//F#
let! result = webClient.AsyncDownloadString „http://REFRESHER.SK/“
Ještě bych dodal, že to není jen vychytávkou Dartu, ale spíš obecný návrhový vzor… Btw v Dartu tomu taky říkali promises než se to přejmenovalo http://news.dartlang.org/2012/02/library-change-promise-out-future-in.html
Doporučuji to používat i v javascriptu http://www.slideshare.net/async_io/javascript-promisesq-library-17206726, vyvarujete se tak spaghetti/messy kódu.