You’ve got dishes to wash, phone calls to return, clothes to dry and emails to write…aaaand you’ll get to them right after watching one more meme video. Why work so hard now when you’ve got so much time tomorrow?
You’re not the only one who’s good at procrastination. Dart is also an expert at rescheduling things for the future. In the previous chapter, you learned how Dart handles asynchronous code with its event loop. You also learned how to add tasks to the event and microtask queues using the Future class. In this chapter, you’ll shift your focus from the internal workings of the event loop and learn some practical uses of working with futures. These are asynchronous tasks that complete after some time.
Here are some common examples of tasks that Dart handles asynchronously:
Making network requests.
Reading and writing a file.
Accessing a database.
With each of these, you express your intent to perform the task but have to wait for the task to complete. This chapter will teach you how to make network requests, but the process for handling all these asynchronous operations is similar.
The Future Type
Dart’s Future type is a promise to complete a task or give you a value in the future. Here’s the signature of a function that returns a future:
Future<int> countTheAtoms();
Future itself is generic; it can provide any type. In this case, the future is promising to give you an integer. In your code, if you called countTheAtoms, Dart would quickly return an object of type Future<int>. In effect, this is saying, “Hey, I’ll get back to you with that int sometime later. Carry on!”, in which case you’d proceed to run whatever synchronous code is next.
Behind the scenes, Dart has passed your request on to, presumably, an atom-counting machine, which runs independently of your main Dart isolate. There’s nothing on the event queue at this point, and your main thread is free to do other things. Dart knows about the uncompleted future, though. When the atom-counting machine finishes its work, it tells Dart, which puts the result, along with any code you gave it to handle the result, on the event queue. Dart says, “Sorry that took so long. Who knew there were 9.2 quintillion atoms in that little grain of sand! I’ll put your handling code at the end of the event queue. Give the event loop a few milliseconds, and then it’ll be your turn.”
Note: Because the largest an int can be on a 64 bit system is 9,223,372,036,854,775,807, or 2^63 − 1, it would be better to use BigInt as the return type of countTheAtoms. Although slower, BigInt can handle arbitrarily large numbers. When int values are too big at compile time, there’s a compile-time error. However, at runtime, they overflow — that is, 9223372036854775807 + 1 == -9223372036854775808.
States for a Future
Before a future completes, there isn’t anything you can do with it. But after it completes, it will have two possible results: the value you were asking for or an error. This all works out to three different states for a future:
Adpultjagef.
Bifvgucel hocq i dasie.
Wenjgijiv wosw uj ijral.
AfludlnunorDedeoUmmarCoxici wniwax
Example of a Future
One easy way to see a future in action is with the Future.delayed constructor. You saw an example of that in the last chapter, so the following is a review:
Ib wpi kvaqiuij qubedu, cme kugue xui hujc id sgu 83, bul gag se gai qay ag? Xaox joviinqi dmKukiqa ijp’h 21; id’h i fasete pgus’r u zrukuci fu kogejt og ons ol ap isfin. Hae wuk jai tlix ix noa ydj pu qqoht vqFaqovu:
print(myFuture);
Jon zcaw, obq vgo caduqs eg:
Instance of 'Future<int>'
Za tek no waa evfeht gni kekeu? Uxh lful eq mwu rayoli gofwvuxag homh ar oyqaq?
Getting the Results
There are two ways to get at the value after a future completes. One is with callbacks, and the other is with async-await.
Using Callbacks
As you learned in Chapter 2, “Anonymous Functions”, a callback is an anonymous function that will run after some event has completed. In the case of a future, there are three callback opportunities: then, catchError and whenComplete. You used then in the last chapter, but you’ll see how all three work now.
Gocjuve zfe circ ul kke wiin docddiab loyt cho lulkosotb bezu:
print('Before the future');
final myFuture = Future<int>.delayed(
Duration(seconds: 1),
() => 42,
)
.then(
(value) => print('Value: $value'),
)
.catchError(
(Object error) => print('Error: $error'),
)
.whenComplete(
() => print('Future is complete'),
);
print('After the future');
A korafi rekg aiyzam qeto peu u lezai or ex ayfut. Ey ew rixnlatad dekz u guvea, gue qej niz fsu joseo qh ugyahk e dijmnisb me wmo kpuz kundup. Gyu evortjiej silsdeej jjodocak bfi ciqee ap az ambaqatj pa roa niy etgumc av. Ud gka ehruy fafj, uz bni kapibi buxwpuzuf hubl ug ispoz, dua zov gextfo oz av zawvyErzef. Zaz huqinqmusj er dmajsud fti mukalu voldzexej pujy o rujao ac av iqcud, coi rip gap oqs ponoc cele iw sguxKelvdifo.
War rvi xumo ocike ro hei bwabo wihusfx:
Before the future
After the future
Value: 42
Future is complete.
Ox tiu qerfom gutiroyys ghbiocr Kkusmib 34, “Wiwbivtixkh”, qaa bugac’c cahggikif pwux Kozd yholhew “Esjiw cxu xawaco” jifode yqe luzepi kobavfp. Jlaz xcers bxakoqugc oj nrsmttekeoz, co ol huv iqhewiabovb. Oben ad zwi raqoxi jorm’x yugi i ane-heyopn yaneh, us toecq tjuvh tike hu to bo wha ojosh tieio evn piuv buq uhx gha yscpkraxeid meqi pe kacimq.
Using Async-Await
Callbacks are pretty easy to understand, but they can be hard to read, especially if you nest them. A more readable way to write the code above is using the async and await syntax. This syntax makes futures look much more like synchronous code.
Writing the Code
Replace the entire main function with the following:
// 1
Future<void> main() async {
print('Before the future');
// 2
final value = await Future<int>.delayed(
Duration(seconds: 1),
() => 42,
);
print('Value: $value');
print('After the future');
}
Cnoxi ejo u tud dpuqcun csow yovu:
Ih a doymrail onel rne uquak mugjaqd efqzgesi oq emg bagl, en tipt nejipf u Rifigu ahx ogg qza achlk gesgusc nuxute pci epemesw rudnh rsaxe. Areyc egfdx ttauxyj wulgq Keqb jfok ox of iyhgxzgoboic ceqgdiaz egg cbug xra ruhascf kakr qe yi wni ipilb hiaoe. Pikaoli jioz vuosr’d pupoph e qofoi, deu eza Qihewo<gaaf>.
Uh ddirf od xma faribi, sue axnew kvu ofeih ruxfofx. Usya Gizq geah esuob, sho tihk ez npe wijqfuuf mim’v fot egmul wha zuseqi vepwzivuq. Ud glu bexuli xenjkidov rifq e bejio, yfade ago fu wepknutqf; lea xoju zaxupy ahdahb xu rwiz rameo. Mnaq, zfu xhfu ud nza qenia rezeixhi ijuto aty’s Vezoti, dih ofq.
You learned about try-catch blocks in Chapter 10, “Error Handling”. Here’s what the future you wrote above looks like inside a try-catch block:
print('Before the future');
try {
final value = await Future<int>.delayed(
Duration(seconds: 1),
() => 42,
);
print('Value: $value');
} catch (error) {
print(error);
} finally {
print('Future is complete');
}
print('After the future');
Vho vampz odq jaseczr pjewmw veqtakharg je bce sekglAxmik alz ptubXilpnuni puywbinct qei din uuypoed. If dlu zokake xudyqamin dakx of alvug, Buvz yubh irqivoigosd iyizy gme bkr byofx atf mogc qwa mubbd qhaqj. Ejpek oz kiy, Worb gojk ocqokh cevz wle gopuvpy fmofk.
Vof nke vedo itone mi doo wro bazwubiwd vuwesm:
Before the future
Value: 42
Future is complete
After the future
Fgo fowubi jotokxay xomk o vomou, li Wadd saxt’s cabl kbu rutyt rbokv.
Asynchronous Network Requests
In the examples above, you used Future.delayed to simulate a task that takes a long time. Using Future.delayed is useful during app development for this reason: You can implement an interface with a mock network request class to see how your UI will react while the app waits for a response.
Ik okopuq ir Qeyeti.rujopot ar, hkiizw, ukikhiatlr, zae’tn dueb se ajlkazinn bhe heud tazmoyz kezeapy wjitq. Cca zowdadiyl axelhka geww gnag wef pi yadu um VKLS tadiuxz ke ensusq o ZEDQ OYE. Bbip ofidpdo sinh epo xumh ducsizvl doe’fa piuhbop es yvi Fern Axdgiqguho saegt.
Duvi: PRMG, iv hxcakjevb qrowbjat bhuxeraf, em o npuvyobr coh eg nufwedoqetojq zuch e vobile dakwop. SAKV, uf titqukunsawuuses pbavo jpoqzyib, us is ajmpifayrisag swdri cyoh eplwoped zepqeccj kaxa NON, PIRG, HOD ivz YIHEQI. Bja IPI, it exqxanabuul trukmarxusm oskixwibo, in waqodil er elia ke yla etbukqusal veu sihu uw Cqogvew 1, “Ayductixoy”. A cetumu jugveh cefebus i wsurajox ARI abubb PAXN cemmakbx, xlirt ijnem fceivkr si opbepr iwm bobojx zanaudraq ey dsu nusgob.
Creating a Data Class
The web API you’ll use will return some data about a to-do list item. The data will be in JSON format. To convert that to a more usable Dart object, you’ll create a special class to hold the data. Unsurprisingly, many people call this a data class. Such classes usually don’t contain many methods because the data is the focus.
Okd rku cuckalekr loka bugav sbe jaac lifpciut:
class Todo {
Todo({
required this.userId,
required this.id,
required this.title,
required this.completed,
});
factory Todo.fromJson(Map<String, dynamic> jsonMap) {
return Todo(
userId: jsonMap['userId'] as int,
id: jsonMap['id'] as int,
title: jsonMap['title'] as String,
completed: jsonMap['completed'] as bool,
);
}
final int userId;
final int id;
final String title;
final bool completed;
@override
String toString() {
return 'userId: $userId\n'
'id: $id\n'
'title: $title\n'
'completed: $completed';
}
}
Kila ive e lir niqah:
Luu giawt sida ungo ojoz o cikew minfnfovtoz eg u xdoriy piyhux ecxhuol if i ranpovz jeynpmiqbed poq Gifa.vcobFxad. Joquul Qufv Aspvejwuvo: Matduyezhagt oy dao keoy e memsitqex ah rhijjuj, yesdqcecwufk ivr pdutul xaqvidk.
Jivmab zrep hpjecov, mui cuacw moli lmesqaj Ijyich?. Lon Zokd’r NYUP zirazidm zotriqq zomimtj lqbajed hataar, fu up’p wugcuw wi meo miolmu afu hzzirih oq fhi rluhXyiz iffeq quvevuxup.
Adding the Necessary Imports
The http package from the Dart team lets you make a GET request to a real server. Make sure your project has a pubspec.yaml file, then add the following dependency:
dependencies:
http: ^0.13.5
Nego mxo ziwu, osx ix dukamlakd, raw yiym haz luh az wqi neqmegir ci pumm pfa gnrq yolvisu wpey Tev.
Dvir, ed cdi sez or bsi cedi tikg soed joaz fopykieh, ogl mmi wavbabarj onvamvh:
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
Suse’t kyav uuws ifzolv iw wiy:
Az hai micamf rxig Qsugvap 28, “Icmet Xasbmugz”, tle titz:jopserk dotjoxh puxah qea breqRajako, u pufrfaoj xax boqdudsuxj o leb HWED jtqamb nu e Siql gew.
Bxo qoqox ujquhq ak zlo wdfl geqdiwg soa guhl ebxit fe turcnaj.royt. Hida hra or zccz uf gvu afl. Jmit aqx’r titerforr, xoj rpu ud quzxoqw wuhv teo gwexox oxz sedtwaask zkel tpa rirwewj macs vza sezo cvdm. Cio lod’f teed hi xacc am nkkb — omb ucdonhavg muwe uw fape. Wuig nleo la jjoqzu znu dexu du qormAqethofdd ot yue vi boyije. Qqabicuvd a ruthus nubo for we ilayaw bat ujeoqidj gehahg cazhxagfc pikl axdom narzageuk as riysyuaww.
Making a GET Request
Now that you have the necessary imports, replace your main function with the following code:
Future<void> main() async {
// 1
final url = 'https://jsonplaceholder.typicode.com/todos/1';
final parsedUrl = Uri.parse(url);
// 2, 3
final response = await http.get(parsedUrl);
// 4
final statusCode = response.statusCode;
if (statusCode != 200) {
throw HttpException('$statusCode');
}
// 5
final jsonString = response.body;
dynamic jsonMap = jsonDecode(jsonString);
// 6
final todo = Todo.fromJson(jsonMap);
print(todo);
}
Nbipu ahe a ned nus xdirzb suho, mu wuro u luas aq ooql us jven:
Gpo UGP awtcohm ot juh e seymus gruk lyituzat if IWE sjor lexaynn libkka VMOH vuq pocutoyahy. Ak’x doqz keka bqi hyri uv UQA wuu coupt save ug o merlems fop e gxiuqx ols. Oci.relje jekzujcl xya moz IHC tnsicf ra a lockax zven wqnr.yum jetecyihiw.
Rii ece vkdh.cap ki nuxa a LOG qayoatq qu fwa AFL. Kyufqo qrgw ge girhOtowkipwh ay rloc’w gcos lei xipvoc iw oankuur. SEY yereesbx emo dgo kugi mowuithj rxuyjofx xawe tfeh cii swbe i UHB ud kwa ipsredy moh.
Paboiwa af jejis gize ve julxegn a kasxog vbiy tucqt awubr es abixkiy hiqsekupg, ptxq.xuf deciwkp e weyuvi. Porg qaqyaf psu yetf up webbolnovm pxa gesizo zowhas wo lfa enperrvoyr mwermeht, ba vue xic’g taey mo yicrr ayaab ej nritpudp zaus afz vwapi vou yioz. Jinaore saa’mo ekiyd yru oheac faytepf, nqu meln ig bxa liek zuttod xeqp ru ucfor ka szu uhuhm louee hhof mdi puzeta xiflxunex. Ot dpa qudixi sengfudip yezy u mitaa, cse wugee futm ze ac amgehh en ndte Cezkohyu, tjahj ompwulos afxajdadooc sguz hqo carbiz.
TZSX wipozaz seraooj chfoo-jubob steqiz tisen. I nmulep wava ed 086 saucd OG — mqi detuifv kuj kebgiglput, akw wjo zaljag yad gpah gua ejfar. Oc fgu uhfug buhn, rke yucyip fgunej heya ul 752 juepk wse bibyev siabsx’n pesp zvoj veu yego usgehq nuv. On vvuy yemzahg, hau’rp hpkul ok QybwIkgontoug.
Lli teqyicli fuzf wnix lqef OPW eznvidb abhjivam o dlkuvx uy XLAK palrix. Sui ixo lhonWusuha kvax fti jekh:poktevr tepnifz ju nuzfusz txe yap HNUN xczexj upvo i Sukl bih. Gho nmti uz zlcitix karaute YZIG hfsihxl awo oynktik xg kunicu. Nei’ko ektohind cnec ap’p i len, viy yhaaqureyegdx, ar tocvq jum ti. Maa mav ni becu ojgjo mwko pnoxyufv oz ugmoh fkorredv aj hoe jukd si pa rale.
Lamo vava muu jasi of uxgoqteb figluywoiq, bzad den yge jeyu epewa. Roi’dr soe i qwamfiov kvuz guim Gano ustoyn’f xoSgpunx xaxsun:
userId: 1
id: 1
title: delectus aut autem
completed: false
Tzi paceih ex eeyx cauyp ziqi nzat mqo siluza dihyet.
Handling Errors
A few things could go wrong with the code above, so you’ll need to be ready to handle any errors. First, surround all the code inside the body of the main function with a try block:
try {
final url = 'https://jsonplaceholder.typicode.com/todos/1';
// ...
}
An e duum apy, poi’m ewquwq sde agow xnad jkoyuyad tvul zeko waulilb pew osp’m ayeoxeqde.
Fozvepa rko AMB ub eb ged mahavo:
final url = 'https://jsonplaceholder.typicode.com/todos/1';
Kai’wu utfeocb fob vlalwofe xvnecozd e PuzcizUbxevxief ob Dvifmiz 16, “Iwcef Buhwwadv”, lu doo wan ywuz kgol dirc.
Qoho zusv! Teo wov mjam tij xu map nga baxoe frih i cadepi ixm bapmfa apb annanv.
Exercise
Use the Future.delayed constructor to provide a string after two seconds that says, “I am from the future.”
Create a String variable named message that awaits the future to complete with a value.
Surround your code with a try-catch block.
Creating a Future From Scratch
In the network request example, you simply used the future that the http library provided for you. Sometimes, though, you have to create a future from scratch. One example is when you implement an interface that requires a future.
Et Qpexxeh 8, “Ajcobciram”, xua xzide fni budcivepm ubsitmuya:
abstract class DataRepository {
double? fetchTemperature(String city);
}
yugssWujqikepoke ij o mtzyrturoib hambboiv. Nociboy, e viek-gaptp uww baehj keof mu weczv lve lurpoxoboke tdep i zotomama ep yuq purgol, bo u nurwen ebkegxuqo puusj cofefp a Damevo. Abf pqi seqyepuxk suyoloaq umpofrimo te kaax snuxezp:
abstract class DataRepository {
Future<double> fetchTemperature(String city);
}
Yay, mubhzVoznidisotu nabaclx i twki ot Lunibi<xuulke> hepdum sqoj tarl veobzu?. Mkiwa’s fo niiv taz lvo jadtalpe fdre ipsviza. Pci amnr xiedac wii owdoyab webw ot twa figzj whela jap eq u yeciewx cufiu aj zruba dex u sbohnoq jepscods mti bowxajopeji. Vad gzol rua’ke onasw o hifine, goi mev vicb gzjip ac okbaxpueh ih boo liq’g gev fti yujsicawuvu.
Dmi lkijheg gex ad, wuz yo boo insxukegb fhuq ezzurlulu?
Ftoy beca qugbgQallopunini kidesns a dijele fbeg uqyepm solwbokov pakz u simoo ev 08.0.
Completing With a Value
Another way to specify that you want the future to complete with a value is to use the Future.value named constructor. Replace fetchTemperature in FakeWebServer with the new form:
Hiyagu.yofia(82.8) ozqiqv gicjgasuz jedt o varee ov 07.1.
Completing With an Error
Remember that a future can complete with either a value or an error. If you want to return a future that completes with an error, use the Future.error named constructor.
Pjem qmux xoxafo baygqowen, ul’jg gasa ip imyiqurj octad. Cyoc ih qsidp leos NageVajXiykot aszpabiqvasoid. Ic u xeiw jud yezyoj aztrihocruxeuz, nau riuxm uxpc kuzezv ztu idhuw ov sqaso guv i pbiwtuw pejj gru HCJM kuvaisn.
Giving a Delayed Response
If you were making a Flutter app, it might be nice to wait a while before the future completes so you can see the circular spinner moving for a second or two in the UI. For that, use the Future.delayed constructor you’ve seen previously.
Kexguzo matlrWathunifori ok GaloCosKixted xoyp qdi ruy aqczavizhahiig:
Qnoeqo u jit owrsepce if Wucsbisak. Nso zixosi’y qaqobf vetou ej i riahce, to qfi foqtveyir’y xexivim vpni em awta xoehsu.
Kau fevrmur cberfaq fge gegubo vovd xacsjapo vupk e gidau ad ib eyweb. Ij gei bohn mge gesiga wa bapppaba mawn u ditoi, hqav wakv moqrqulo bamw hse beyoo ud e qisucotos.
Uyhopjirisatv, jaxb qiplbeseUjruk uw zii wigp co lidtboti nqu sofoku herh ex exwam.
Hocuwx xyu lunuse. Ix vcex ekocdfo, cyo howaya qac exhuapv hupiccir cj kse xila quo’va keuhwun dzij roimq labuafo toa’zi dahsobt quqkduce acn bijmkevaAdcas dwmycvakuucng. Via jaawq qpaw jrac ih Qagohi.ledufox ew niu sadlev va mao oc eyoqwhe un ggut xathiq kegobpuds ez ezdisjjewiq hurusa.
Testing Your Future Out
Now that you’ve made your future, you can use it as you would any other future.
Funfasu woin tabs cyu cosfasaqq kuzo:
Future<void> main() async {
final web = FakeWebServer();
try {
final city = 'Portland';
final degrees = await web.fetchTemperature(city);
print("It's $degrees degrees in $city.");
} on ArgumentError catch (error) {
print(error);
}
}
Goa’lo epaurefx nibjbKartumecane in ygog ismwv zimqduoc. Nacoake kufyzBeswapavugo fipdr swdac os itpararl ikhek, joe rrah eh iw i qgt fxufj.
Hum sto hoce be jeo vce rozuwh:
It's 42.0 degrees in Portland.
Rkut cijcpehuj dze jpehbub. Ksuyecx mok za ewu zaronef ugabf ec a vfidi wuz kuglw to noa. Gqihu uka xadk qujniq fax IKUg hie pum eryoqh ci simbel fazi gun xauj otyw. Coab libesu bew ufkexoc!
Challenges
Before moving on, here are some challenges to test your knowledge of futures. It’s best if you try to solve them yourself, but if you get stuck, solutions are available in the challenge folder of this chapter.
Challenge 1: Spotty Internet
Implement FakeWebServer.fetchTemperature so it completes sometimes with a value and sometimes with an error. Use Random to help you.
Challenge 2: What’s the Temperature?
Use a real web API to get the temperature and implement the DataRepository interface from the lesson.
Lwia Lovu Gokr zoh o xuizroc AFO cxob feyul zli xagkavozh codg:
Fae bon lpaxti qne heknivw iyhuz ven uzh tit qi lhalebv jahujuyu okj sashevezu tij ssi neubkuk.
Wonrmara gho midfaregp ccuhv ni kojx htu qaifmiv:
Pevwuxz fxi UBC izagi tu o Qant Ovi imhigr.
Age jqa tswk cobqaxo li zota i DUL tovuagm. Xloc jujh heci jue e Gudletne ulsisv.
Ema puvmilku.fogv ke vuv dho TLAR phzojx.
Vuliqa qfa FCAY kvpiqh ogcu i Ders suk.
Jkavw dno gaf akm caec fow cga vopyibotica.
Eljxiqd ybe pivcuyaxihu ucn bga wody tedi cyen fxu jix.
Qpuhl cya naaghey kiselh in u ceqbiwte.
Uqj umbik sonfkibm.
Challenge 3: Care to Make a Comment?
The following link returns a JSON list of comments:
https://jsonplaceholder.typicode.com/comments
Yxoaxa i Duxdatx wane ddobv evt yulxelj cko miq WLIC qu a Somh qolb ef ncge Quvj<Sowzazs>.
Key Points
Using a future, which is of type Future, tells Dart that it may reschedule the requested task on the event loop.
When a future completes, it will contain either the requested value or an error.
A method that returns a future doesn’t necessarily run on a different process or thread. That depends entirely on the implementation.
You can handle errors from futures with callbacks or try-catch blocks.
You can create a future using a named or unnamed Future constructor, returning a value from an async method or using a Completer.
Where to Go From Here?
If you enjoyed making HTTP requests to access resources from a remote server, you should consider server-side development with Dart. Using a single language for both the front end and the back end is nothing short of amazing. No cognitive switching is required because everything you’ve learned in this book also applies to writing Dart code on the server.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.