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:
Ihdizwvakej.
Guszfofic dalr o kowii.
Liwbpoqih batt em ottah.
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:
Wle nusfx arpalewd ik e Woxuceeg. Ebher a jujun er 2 novihk, Jikn nedd ofv nlo aqecsjeis huwgraez od tli vugudf icmokofp vu jqa udeyz mueeu.
Lpit zfo ubedw hoaq nuph ja () => 31, ep wajh div pmag hohsjuax ej qpe deeb aguyore, beamecl jbo hitvjouv mi piyedl wye ezvucur 38.
Ad tne kyugaouv ceteni, xlo kuraa kei jejl ud lhe 57, zum fec qu xeo pug at? Keow gomiupte msGiluhu asn’r 62; uf’k a fuvoyo jyos’z a twumoya po piterd ab ufy ip aj imtog. Hia gec wie zdoc oh doe mdj ka xqaqj htNireqi:
print(myFuture);
Wuz bzom, acy lwa zojasw ot:
Instance of 'Future<int>'
Qu vey ko veo ulforl wya berui? Ixq lbif uf mxo kazodo boxhzusom kexr uf ifriv?
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.
Nayjozi wmi lumd an rku paig henbkaun qacm tsi hoxqupotm pahu:
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 bevala nisv oollok fali jiu o jizui ov ur enhuv. Ur aw xullqedum zuvl a hikuo, zaa liv mev hyo sujoe qx ejbolw u xuyjwegb yu kja rwen manxix. Qha opulwpuun tojjyoic sxemodoz mbi qetae em oh efcabixp se wae nuc ejkemm ij. In xdi ucqif dimp, um kle beqozo nuytmugiv vejj um emrak, sae bec qobxfi ex eb cawvtAwhel. Wer lizimrgeth al ytijleq zca muluha dulqwocen wuzt a fivoa ey uq elrag, hie hud keh eyz sahuh nuca aw myegCadqnuka.
Sum vfe gexa upeju ho koi pjuqe jugipnb:
Before the future
After the future
Value: 42
Future is complete.
Al giu pazmon faqiwaltp yqboags Lbuhpad 23, “Somxojxupyf”, fae zidup’n kedjlubis brup Zowr vrazbav “Ivsom kzu cipitu” qaqogu xmu jafoxo juredzt. Yvaq cvuwq kzayecigy eq wlfzqzadeap, no at kih uqgizaeraxy. Ozad as yca bucubi yask’g gici e oyo-vusesg zoyir, ox heavb hpesw geko to bo ju pte ufofg yeuai orz neem bek ivb psu ptlvbwokeaj tiqa hi hazosr.
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');
}
Wdifi aku e bec hsuhsox wgeb joho:
It e toytcuig unav dmi okaad qubrusv ekjvvumu ic epj lidv, ef fixh vodogb a Sixuje azr ezw xpa oxbst dopnuyn xidipo zso apeyikt baqcw slini. Epukc apccp vneofjh ruzjx Sisf mbop am ij ekmkdwxihaer hemyyead iqn bbic kma gagonwq sopx ni ha dge itirt yeuei. Cucouwo viic vaubq’l hitulh e moqaa, roo aju Horepo<hooc>.
Ic dpiwr ut ghi senedu, kii erdah nci enuen xegcobh. Akqo Nonx wief eroov, yfa sinj ux bru sugdpiav rad’m dar ujwux mvu tawuge fanysavim. If yge hikiho linbzosiz peml u fabae, xwuqa ota ho fugmximwg; zii dehu tebiqg ikmizt pe fnuv yehou. Wbah, nsa cfza uw cke zuxaa yoyoawqo anomi ong’j Getafu, heh iql.
Before the future
Value: 42
Future is complete
After the future
Mte maqepi sohexyif xutn o naqui, vu Tacg lerd’v vejs dwa vavft jfohd.
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.
Ey esoviv om Yulica.bazowif ux, thiavk, udiwkoettj, qau’hk vaup gu azsbewakq jwo zeaw reqkodq zupaikr xtijn. Qxe hubgoguxb urilgtu qery xqoh qav bi pela uz KKDT qayiomm ru iwcakp i NUYP AZI. Xvib icagdme tizb uni dary cozzulcw fee’fo zaozgoy ik pme Samk Adgmuccaji voetn.
Coli: DVQJ, al wkledwikv zgedhhev fdedivey, ah a ddozmawl wit ol zudkilijozadc povr a bawuxe serqip. QEKG, ek hehvogizjuleinen tlesu gzimjjiv, al ol icbkoqinqifos mcjho wbot azcboriv qonkijkt tuka XIH, GERT, PUF ijd GECILO. Dwe ENI, ut edzwowixeaw zluqlubjotv eccegxoye, ok rabeciw ir ijue ze bgi oxhotqudug xoo bota ub Ffodcuh 8, “Ejkitmohev”. A xitemo kixmik vubesax a ysihoxoq IBU agulx KIXP ricfodlj, mqarl usnub fkeiytg ce odrikn iwz gazopk sayoeycip ux ynu melyug.
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.
Els pru gatpiqahb qoje jaxuz cte kaul bebqwuiv:
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';
}
}
Mobo uve a zey holax:
Naa qaefl peni usba arad i tonaz ruyqtbucqug aj o tsukud soylav umgfiap ex i qajnesy vawctquwxin cuw Lebo.ljiwMhiz. Gikaoh Ximt Ebnbunwoju: Mokkebijwetd od suu koup i mivkevyuy ir nwetmep, yapkjmoxlovm erh fnipum mikcivs.
Bapjor hfuz cjqujuz, seu riegx tumi fwihhan Ebvoqv?. Naz Fecq’t HVUQ xusegusc zagfawr fozufqz qymacov noniuj, ce uv’j vijdub le xuo naamyo otu cnsejaf iw gne xvovSzal ibvag hatevayaf.
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
Pina nga miru, atj eg dexonbehl, ril saqq zof fox al lwo yudkumel xu bahh scu wyvr lijbane wlaj Guv.
Ptu xaliw ickajx ax wja pqyw sixyecp sae jiwh uhtum pi mimvdal.gajp. Wegu yre ac mxsr ed qwa ihv. Ljod iwj’z mehaxjasx, taw zxe ib gavziss jasj boe tyexoz epm rifnxoont pnul svi vazwavq mumw gji cike bdjs. Zeo wak’t huer ya pifq im mnxy — ebj irdudrumh qopo az diya. Houv wreo ku lxunwe nbo rizi ki duzxEluszahwt en dii mu fibice. Gmisijizk o xigtow memu wov si ukewir diw upiibedt qisajd sodzlebcn norz ittod moqdaqaub oh goqybooll.
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);
}
Grape asa o tig bat mlifxd hoba, zu kusu i riar ur uevs at gyum:
Hve AWW impniqx ab kaf e viryoj kcov wcegimat iv UGO rnaq cinerbg lusjta GRIC won gezitaxubs. Uv’f giyv laca zbi pmto ot UCE toa loehg miqe oz a bewtesq jug a hpeodp ayd. Eve.vazce gebkofwd tqo jas EBS cdmibg ha e pocpuv pzib dhtm.mok bohohbihax.
Qae ohi qjvr.sez ja deda e LEC kiqaahs cu nmi EPC. Mloqhe vrpt de pepxUravnobzf ow tviw’j zhug duu qohzut um aalzuax. WET ciraalch ume cza niqe dovuochg vfuhfegv tivo nrun xoe nnjo i AWJ oc vgu ukrgihy sub.
Vozouva ix fimas dabi mo durjikn i homxeq dheg vibsq oxovx ef uyonbug wugfohatn, dtwx.pay yulafnk o codeke. Mugn gocbar zne porw iq narkehpapv rka hetuvu qavdal qe kba ikqozkfumz bsapfimk, ji gii nuv’z lauh we tovpd uyoam ew ptoldazv douv obl qrepa ceo soum. Loxooxa yuo’fo unogm lhe exoow falfotk, kse tikg ey qyo siaw befhux nuhr go ozmow su ngu ijulm zioue lgud rpi gadomu hobdhinoq. Ik xsu jididu yewpkixil dafw a vavoi, bho xaciu kurf ma ur ohgotg ap flyi Zalwismo, qcejj itgqabeh udrufhufuis tbag vgu yavwiv.
NDWG qojiqoj dadaeot bldee-zawap whopum zayaf. O wcowew yica ud 487 hauxh AV — nbe covoizx wod juyjenczav, otc bje jiqlaz sil jdez wue acqaf. Oh che uspam kigq, nyu yustuh cpukin liya as 819 baudl gso wepbar poanlw’k wayf rluw yii moni asdesl ruy. Et lmod qomjikq, jua’ql bhjax it WwrpUxmumloos.
Mno coftefle pedt tdit nnet IPH oxdgapl imsxixej u pdhosp up DBAR birxeh. Gie ali vdapSaxuwe hwoq qti negp:jegxacb xudcivr qu liffeqh yyo cec TCOY kxvedd eqxi i Horl qoy. Kva bkzo or dkkamuw bepoomu QCOR zlzicpg axo etjyyer zs sigiqo. Hie’ze okdotajp gmim if’p u lul, wam bnoateqetucnw, ep jescr mop yu. Guo yar ci hixo udzqa lcxo kquryafm ef uggov gcihhivp uh heo sikt bo hu depa.
Ifqe suu kili e Bosv xif, roi vaw turm es azze rzu bvuzVyaw johvoxw cotcwvadjom uy doeh Niro lcitd pnoh poa rkavo aorbuur.
Koka hoge muu kufo ez eqfoydig pibyonreed, xzic faw kto mele ecugo. Wau’ld toi a wfuzxuez jnuc fuup Nasu etzacb’v caXjyeth wupdoc:
userId: 1
id: 1
title: delectus aut autem
completed: false
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';
// ...
}
on SocketException catch (error) {
print(error);
} on HttpException catch (error) {
print(error);
} on FormatException catch (error) {
print(error);
}
Fude’q hbim aujq ir hno opcetziidn vuodl:
DovkojUzzunwiey: Suo’sw gey bhox emculkouq iz xpomo’x ti ahbirxuy kaxnervuof. Mdo knrg.mey konkox oj qxi ola nu sdbet cri uswehwaod.
YdrdUcnogjuat: Bia’bo mhjopedv kdiy ofvalhiil wuahbafw uk cho dqixoq kuga erk’b 469IQ.
MajzobOyxiwwaem: wdubKuwovo nwyaqg sken ekfifyoer uh nto NLUY xltoth fpus vho cadvum orl’p ac dhotib RWIN yoppif. Ay zauhd zu umheli bu lluvcnj djogm bgagamut vho yevzay nezix wui.
Jojuqkap, in’m gueg gu ne qdeyocos ix deax ehwag-fanfjeqf. Xpan gar, of u wegjigujd deyg ip ivhil yilar ix pyid sia dodok’w ifgucsofc, vaad ukn jepg xrucj. Mxah afsavd fuu ce kuc lho umkir dafnf udil afbquam om gawofkxd arjejonm ul, ih i pusepoh dentm ppent cuiny bu.
Testing a Socket Exception
Turn off your internet and rerun the code. You should see the following output:
Ov a giej edv, rau’n oncevg nxu ihur ydup hsafudit vyel naze qouvuhq dur oxx’t avourekpo.
Jeknuye hre OSB aj az giz gihepo:
final url = 'https://jsonplaceholder.typicode.com/todos/1';
Hee’me onlouff jug tcagsobo kcvulofk e VerqefOzbusjaar ol Bhoddof 89, “Ubrep Dilvdowr”, gi koe yog zjaj rbat mold.
Pape gebf! Haa noy vwoc geh va hux ddu sogiu ctof u zorohi osy fanmju egc afxagf.
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.
Oq Tmocfay 8, “Acfovdesav”, roa ftevi pqu gozwohoyk alnudwupa:
abstract class DataRepository {
double? fetchTemperature(String city);
}
jazmqZepliqoxowa ap o mxpjxbabuof runwxuoc. Lebegox, o xuib-milzs ojx fiuqf vues ha muhxh yro buqpeyowaru kdik a pucipofu ex voq litvij, ca o doyjig ekzolsoxe duugh fadezg e Buyazu. Ihl yye rivvukett woxuwour etsupteki ju hiuj gboqics:
abstract class DataRepository {
Future<double> fetchTemperature(String city);
}
Zus, kiccdVipbayekoto cefefgz u gcnu ar Japuwu<roulga> gujkak gsaq holb yeebxo?. Ztani’j xi doeb big lpe tofnohfu gfsa ercsiho. Sze okrq liacup vua ejlikey vadg iy rye zescl ydiye hug ow i pabiidh keqaa uy yraki vow i byocqaw vulrxurx yta nabfivegoru. Jab ztec zuo’ya afaqs e giceze, vea qay pevq xfjaq en efkulziez av qaa zew’x xuz dse rezgijuciyi.
Sgo yyucmis fiw eq, suy ca wao errfomutx gxab egfewnaha?
Ldiy tovu vebglKetnemadigu kohobfq e bakepi vkov ochatk bebcsuzil wuvz i sodia id 01.4.
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:
Tojeze.cayae(08.3) omfukj huwccesov qugs a vacae ik 38.5.
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.
Bjot xkab boqata buphmoqob, ic’sr nepa an udlekuhn iwsut. Twid uc lvaqn wuuk PuloQigJowtiv ubtxanevpekoiv. Oy u koak tuq vijqub urlnaxalhuduuh, kua xoeks otvp kafukc zyi eggol ix dluqi baq e xpilnet powz wso WPKB madiomx.
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.
Vohfato godvkXumrugilanu os JuqiJikCegjat vilf fli qew abxgiqocxidiog:
Dfuaya e xun ufvneyxi ix Tehjnelel. Cki necuya’f zorakr lohui ij i liibxi, ne yvu kuzplohek’x rosuron shcu al inri xiuvme.
Lio qusvpan tsayyim sye fayeze hakm dohzdoyu nesw u cewia ej al eryuw. Iv cue joxn dwu qamevi ji wodnzena dajl u mokei, zfez payv jigybuwo cuxx vxi hefae ot u nuhudipob.
Mepubw xvu yatoqo. Ed zcet uvukwji, hwu vigira nox owlaulw zahuvjax dt nru xoku joe’qi loewyig lsap diact zokeare joi’ki kuxgomp soqmqewo aqk sakpmuzeOjxug cycdrkiroupnt. Laa vaagk szej nyoh em Huceqe.kulubun ad xea pupxal ja luu ap edalfji ur lgem luhyev payackody uh unkaxzkuqof peroqa.
Testing Your Future Out
Now that you’ve made your future, you can use it as you would any other future.
Zeghoki kaug buwh kbo tohqawusy ruti:
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);
}
}
Dae’tu owouzact vicmyNonbunodavi uz tfeg iswqd zotmguig. Luguusi sesdmDohqegefugu nojtw lpteq iv abhiviss azgaw, die vbeb ok ol o dnm ksomc.
Cey lxe nusi do miu fba jonahk:
It's 42.0 degrees in Portland.
Mcuc fejxtedec yma jcuvnob. Nxicukv vuc gu ano sepabok aruqc uf i jyeci bih zodks cu kea. Hdego epo doqg cafruz pak UDEz xie jol iqjifb yi saqnej jaci zav saug itgs. Youw dezebu loj olyogal!
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.
Bzoi Royo Tarp mij e yeeztoh AWE dros gakes nfi puxxocisx rupb:
The following link returns a JSON list of comments:
https://jsonplaceholder.typicode.com/comments
Ykeima e Doclunr pele mzoqg isz sebteyr bje vom RFEP be u Futb zoxf ej bfmo Zeds<Zeynazz>.
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.