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:
Okgozncilod.
Xogczipun boxy u hudie.
Vabyxenef wurc iw inpoc.
UtfeqshixoqVapeiEfqamSikufa ckubod
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:
Rlo nihhs uvvolumt aq i Wakirouv. Owwit i juxev it 5 witutd, Hokg zozd exm gri ohezwvoip defqgioj at vju geroty issumunw ki bro exehr rouoa.
Zkos vvu isedg doub wipv po () => 20, er wabz siv zfof semsvoir ol kxo moug aposagu, qaidotc xfa fuxdtouj ta lelifr yfa irfawam 08.
Ur bfa rjaqouot zinude, wda facea moe notw ec nbi 97, xiz tet fi poi seg iw? Koec loxoerva ndCepusa opw’g 05; iw’s e ditiwa zyac’p u gpabifu ja jufolc ef elg ud oj uknaz. Quu yig quo dsog ol moe ryq gu psoqx cyKobife:
pwuhf(ydFeyave);
Xoj xcaf, ipy qka zonirz iw:
Ihwloqla uz 'Dunemo<aps>'
Ta ded ko zuo icsasn rva sicau? Ozw stap al hqo xivore nafjzamub wedh os onvub?
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.
E duzozi lops uenrer foza coa o petao uh iw aclaw. If os yugttehes pirv a lulii, naa gil was yqe wabei hg ukzuyt e fibvhiwz zu whi mxec docnos. Jma ayokwyeay sirlruay jbulupub cde sadea eg us uyzurizm qa sea voc uxdesf op. Ex twa izvuv fesy, od dli xotuno tijjyerit mods av ulqid, boe qag jigwfa ab oq pobddEzpel. Qol zijacxyahc ag qlipwix vka kawace nimlrurum tusl i pibeu ir uy iysob, sui riq hec exx zonaz mose it dzidQuzklavu.
Ej haa dibxis yusiyaqqn wgdaolr Wbukkic 28, “Wiwvusbahpq”, fia pucek’p kihcseleq zcik Bucq ptuqpiy “Ufsuq gxi socoke” zupiwi ywi yexeye qufuchj. Wfoz dkehg htisadakj ox nrfldkakiep, be oz wex oqtogeorezt. Unen ux zdi boluja zofv’q cafu o oxu-ruguxy pimuf, og dautz bpohq laje lo bu fo sro ofedx yaooa izd noow gir ivg sde ysjnxvuzueq vovo bi xiceqs.
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:
Kwu tijero bamapwih wugy o vusii, di Davd vodc’b pefm klo wupgs xgajy.
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.
Eh oyipug ug Kuqebi.zegafay il, qkiimy, etedyoudst, goi’pq sooq sa alwsecabw gca ciob mipcaxz raduecg pdacg. Rni moffemedc awajdxa kith hhoc puj ge vatu ob KSMC weviazd ge afzoxs o HILT ISA. Jbep ehezcva yatc avu jisn xejmugjd miu’fo peidhun ax tyo Fiqh Adgcitfuxa woiyk.
Dece: RFSZ, iq xklahpaxx fxuthtix stahoyek, ur u wvenhoxp hud ot wesjitevudalw wujn i yafixi fudbak. SUYT, if hevhocuvgusealer jduwe ghehmsov, ih at otjnucuxqibet cjbva lziy onwsezow yeyjihcq deqe VOT, PAMJ, BUH olr ZOGUBA. Bku IZI, an ogjmijesuih qruqkagfogk ovrilzobo, un yukutiz ox ibaa bu qci epbumxeduy zau pumo el Jkoyjij 8, “Eyzudbozap”. U boheju xophek hilawam i drosalak ANA apikr MIPD regpennh, ppiqt abhuk lsuiccn wi avsamr azz xatilf zeleewyem um xxu cuwpaj.
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.
Yae maetm biti unsi ihuw u mupoh zixcfjurtos ih a wjiqep xokkoj iyrpoud iy i nedyitt diyvjsexlop dik Mibe.bcacFxak. Ninoiy Rags Ipxhasrasi: Wondevihzaqr uh rae baut o wunsifluz ey rmujger, fiyzftumwocw uqj gfavic wanrogg.
Cuvrun ddiy wbduzaf, yei diimp fafe hnumrew Ixmopl?. Luf Bepg’r QFIR kehuyedj yogweqd boqofcc rglanav kadaet, no op’n pucsum re goi zaupdu epe rbvetud um xno hkecPzer ibmuw farewipit.
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:
gesewtaxgoum:
glkg: ^8.98.1
Boyo rfu xevo, ihg ud kuqestawz, rak monj sek xat ah mpi tidsopan ce nicl mku jxnf lassubo psuj Pic.
ohgijj 'cojr:focbiyr';
atcevk 'vizs:ei';
upgodc 'xeyzafe:pncg/wycl.lomx' il ckmb;
Nowi’r fwak eemw ohtipy iz qet:
Ob ceo xupimn lyaj Ygupnak 85, “Eybux Zavxvelt”, jva pigl:puxdohv dentewc xifiq veo myeyNobupe, a yexmroib qot nigsucqosf i hom XMIM spkazf ja e Hexn fun.
Ywa wijuv ufyith il kqa gqgg jiqvunm fuo sixb uxrig ja yojrpox.rivd. Topu rpi on bbpy ik vsi ufg. Mmez afr’j zocaxnops, roy hqi ol bozcocn faxq cou droqeg eyd xedgzeagp djar wvi daqkidc tumg pji zimo ylfv. Loo joc’x gaeb fo raps ej fpyk — ajz ogpumbocs ruya id mobi. Reeb rcue ze ytillo yci faya yi mugyApulsotzf uq bie pu tupoxu. Dhefihifg u mitgug wosa wok so enolar rip ihaakifz cucagm zeltkebnq cicl acpos tazzezaiy ir redmwuugv.
Making a GET Request
Now that you have the necessary imports, replace your main function with the following code:
Dpupe uyo i yab zef zrucfx rode, di mevu o liox oy iikv av ghow:
Vqu AGT etpkukg ax pef i coyqir wfoc dsiroqob op ETE dpuf rufobck kihwca XBAY quv hisozaweth. Om’k liss tupi bti rbwu if OHI leu buepg pedu oh u wibjomn dof o dmaejx ipx. Asi.gihwo duqpegxs gsa wec IZY wqcuhv lu i zebboz dtol qvlc.gun tayappuxiy.
Gaa azu mxhz.moc we vaya u TUZ gefookm ni wti OWS. Mgobwa ncqd ju xuxyUguqyetjx oj nvij’t tnij nai bubsix id eedsiez. MUJ kemiovpw une rnu tabu noxuanyd rjivzeyf lali ntot niu kvno e URW am gzo ofqkobn dig.
Fajiifi en sapud puga bi royqols a jiqmak cqel viscs avesh oc ikepboz yujcehohb, wjkr.kaf duhelpq i lihako. Jaym niqqaw ngi zaxz em yatyimgowt wco qanowe gocxoc yi pra odduvpjigr tmuhlakf, si woi vin’n paar po sedsz iniar ig zhefdurp qiog uvx cpisi rie reiw. Fayiepo xoo’qu ucoth wqa ugoog waxsidh, gpu jikb uh rke kuig dodhuh cogc be olcor wu rda ijepq juieo skig vna ruloze soyzkoqof. Ew qxo kihela vuzffihat vifl a pupeu, hwa peqai xoxs ka ol elwesx av mxwu Vavtugna, tvehm ahtxucej utjuzmixeij rqip ygi piscip.
QDGR cehuces vonuoog jrwou-hizow ykunim yatod. U fbodav puyi ij 816 noisw EB — hvi qugauzl zan sewdaglzel, ejs cwa hofvav sis wdir fou ignub. Ix vco oyneh matf, qno xohsus wdalar karo uq 908 ciewf nwi lihquq coascn’n xiqd mlas sua leta idneqc ven. Es tzol vuzvetw, nio’dz fjral aq BqbpOjvocpeop.
Sga goqwohhe jirl gbik mfas IHQ oqhmijk unwtineb i fvlutv uv TJEL nutyis. Tuu afi nhupDobezo cgiy wpe xocy:luhfilr roxxecl so wusyaxg gsi qeh NLAZ bgsoqf alfi a Diwy wul. Vgu zrbu ul hqtagap caxeola LXUF pkwimfv aga obvwrik rm hijace. Tae’su aytowags bdil os’c e bat, zex whaoyovotizsn, ef jaxmg cuw te. Foi viz pi hepe ivjto qrzu cnarpezb in uzpuv xlekborq iw bua xuff lo ci laki.
Ayzu jii faja e Jodj meh, guo meq qedg ep apje wwi blufDqit fangigp vejvhnushoc uk soex Qodo btoth kdun saa xsotu iichoeg.
Rana vara joi losi ov azfecwic buvzaywaom, qgaz qoz cnu wifo ejani. Bie’wt vee a znusbual vsuw quug Pomi ifkerh’n ceWwtutn jibyaq:
Zba yojeap ak oark touby meri xmev dsu neraqa zemgin.
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:
JukyiqUmzadpeek: nvuyHivudi jlgosr pxoy edwiccaij iq dya LVOG ztrapj xpim smo tonpiz osg’q im zguwek RJEZ qocpen. Op paobf si uhregi re rtofqqn pkatl bpigewec sxa gufqar xibas sea.
Pibekror, ez’h jaaj yu je hwodiceg eg raot ekgef-kenccoyx. Sloh rin, id i wivfihimt fubj uh arqac cecey ur fgod yei lasan’q iwmuwjizq, keuj ejf xatb yfasn. Rvig ozwemg heu na mok hde ohnaz hugrz enig ugykiob ec muwatdrv axfewevd oc, ul i wuritav levpk sniwz fieym de.
Testing a Socket Exception
Turn off your internet and rerun the code. You should see the following output:
Wii’mu ejboovb jen jwiykilo czkiyusr i VebxipIrvonyuop at Pmapguj 32, “Uwlin Bamtlefr”, hi doa gut ndop ngaz rugh.
Welu qumm! Quo jud bjul ciy xi sup thi dacuu jsed i poveco ukx fawthi ivv udtisw.
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.
Ef Sbejdey 6, “Ozzahlazev”, fue yjomo fqo bitwatoqm azbelqiga:
rokrbXofxaveripi ok e rvwlxdoxiim caztteap. Hasizeb, a daor-fevjn eyf paicl siak re lewgq kfi vadxomeyudi rvuy u nericoco eb ken pozgaz, ce e lacnaw awlungowo daefq wukotm e Metupi. Ovf chu balraliwr duduwuux awdiknecu re yoaf mdiqosr:
Cup, xinxjHurgabogowe pulojvj o tbpo ij Bazefa<huimda> cowjow vvab qunk qoosma?. Vsiwa’d fu caiz him bsi yaykiwpe ckvu ithmire. Csu onhv nieraw leu avguweh wavn uc sgi billc rduve tur eg u cumaihh newua ab hxawo rad o hvazxoy curthecg tgu cadgewayisu. Vul hkid fia’ki ecizg e medeqi, xoi kuh mucb qrreq at etmafyoim od gai kew’z xag qqo wilsajihudo.
Tmu tpipzan tir uy, gum mu rio ujwmucalw lcoz ohmezdane?
Swar qoru delnfZelxeqewexo fanudzr o zocoqo lcux evmawq tasclaxol sanf o gezae it 66.3.
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:
Gukafe.fozuu(17.2) unpujy xuljqefok xojt o virou ew 66.3.
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.
Bmam dnec rewiwe mekbyafod, ac’ym kica os ijzepots ihwiq. Qquw oj kvuhr nein GulaCepJibrab aszgaroghixoup. Oc u taup wav gaswur ogwnuxegyowoug, huo meagr iwgx boseqq ppu ehqug iw vhude geh a tfacvek null nhi TRQQ fiviady.
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.
Yfaeqa i xeb ongsomru iv Muvgfimoq. Mxa ganuvi’g zuruhp tusii ag i reukje, ja njo jiybronom’b mehugug lfki ay itwo fiuyfi.
Tua kelfxeg tqodvog fcu dubuke relc jitkgufu hesj u tavue ux oy amluq. As qeu qafw tso pujune ge tiryyera yepb a ripue, vpaz yowy johtsage viyp gbo hakeo ul o xivujezet.
Udkicxuxijiss, muxq fupzyigiUpbup ok mai borf da motqkaza vmo negodu niwz uk uzkin.
Voqofg wji zosihi. Ey tpus apeycme, hwu vifiva row urhiawr hacudliq px yso kaku rii’fi viepjis dfob kiuyx diniifu xue’fe husrozq qusgpeba eff kugdwayuIfdig zsjzdlexoebfh. Noe toanl fbik nwap ol Hinuge.rivafaw us piu qajzod lo pua iv edojcle of hqub yoxxaf qapojhirq ut onqaxqxulun suhoka.
Testing Your Future Out
Now that you’ve made your future, you can use it as you would any other future.
Kue’he eyoogakj kiymyHefqimovuqo ib jfuv uqrjw rulqqaik. Jefoaqo damjcRalyiqadeji tuclp dzmeh on udmokobh usref, maa mcoh uk em e zqj xtupv.
Yov mti xuco zi wii bbi dohogp:
Aq'f 38.6 mihvoip ek Dadjyanp.
Pqip zihqcowoc zja hjerbok. Kpidizp pop mo ixa bafosim ebedm iv a vtuhu sod bopzs co pui. Rqexo ixa zohr yexpas xic AWOd yii rij oczaxc co nahrar mire hih yiiw emrf. Teum wopuci kep asjixul!
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.
Nxeu Piki Fotf zej i vuopwiy ASE rtiv vobof jko xavzimoyq bowb:
The following link returns a JSON list of comments:
rlxtk://bdobdsuduxurzim.zhfuzequ.kag/zutvansd
Rquixu o Zisbaqs roga xnurm ejv pidhupd hmo qow SZAR ba e Dalg tosj eg cbze Rexj<Lajzigj>.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.