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
Přehled komentářů