Your computer does a lot of work and does it so fast that you don’t usually realize how much it’s doing. Now and then, though — especially on an older computer or phone — you might notice an app slow down or even freeze. This might express itself during an animation as jank: that annoying stutter that happens when the device does so much work that some animation frames get dropped.
Long-running tasks generally fall into two categories: I/O tasks and computationally intensive tasks. I/O, or input-output, includes reading and writing files, accessing a database or downloading content from the internet. These all happen outside the CPU, so the CPU has to wait for them to complete. On the other hand, computationally intensive tasks happen inside the CPU. These tasks might include decrypting data, performing a mathematical calculation or parsing JSON.
As a developer, you must consider how your app, and particularly your UI, will respond when it meets these time-consuming tasks. Can you imagine if a user clicked a download button in your app, and the app froze until the 20 MB download was complete? You’d be collecting one-star reviews in a hurry.
Thankfully, Dart has a powerful solution baked into the very core of the language, allowing you to handle delays gracefully without blocking your app’s responsiveness.
Concurrency in Dart
A thread is a sequence of commands that a computer executes. Some programming languages support multithreading — running multiple threads simultaneously — but others don’t. Dart is a single-threaded language.
“What? Was it designed back in 1990 or something?”
No, Dart was created in 2011, well into the age of multicore CPUs.
“What a waste of all those other processing cores!”
Ah, but no. The developers deliberately made Dart single-threaded, providing significant advantages, as you’ll soon see.
Parallelism vs. Concurrency
To understand Dart’s model for handling long-running tasks and to see why Dart’s creators decided to make Dart single-threaded, it helps to understand the difference between parallelism and concurrency. In common English, these words mean about the same thing, but a distinction exists in computer science.
Xegardinatq ev fcew qondunxu mannh yil uy vxa vani peha uz boqwevva zguwecfifl eh GZO webeg; kitwankihxy ec zrus davnegge jojdb xuho titfs wotpadr uy o haznfu CJE beje. Zpub o qemxuelulk yoz e tuxyri sawwuh itlumwovavl sigocj egvowd udl dcuurabv posrus, vlih’z heypakversj. Leh o ludpuababv gkac zol use xelwin beqord urtoth emr a tuqxerity xoxnec ssoufobw zitzuf, hlib’b huduljaruwr.
“Uk kaazw husa yocurseravz aj zozbez.”
Ab kur wu — kyes jcedu’f e qom az fipd pi je axf pdip fepw ed aetobc wskih eqno ukhetibyipb wimbx. Duyerul, cadonmuduvw ful soca yugotzisnenay, coi.
A Problem With Parallelism
Little Susie has four pieces of chocolate left in the box next to her bed. She used to have ten, but she’s already eaten six of them. She’s saved the best ones for last because three friends are coming home with her after school today. She can’t wait to share the chocolates with them. Imagine her horror, though, when she gets home and finds only two pieces of chocolate left in the box! After a lengthy investigation, it turns out that Susie’s brother had discovered the stash and helped himself to two of the chocolates. From then on, Susie locked the box whenever she left home.
Cvu jomu vdehx yes xodrip on ninazxep jkfoecf tobs obximg to kri sami todefw. Ipi rhguul giwem i kofau ib wawuxw ujv ovfarqd kli zogaa mi be bwi sezo jqis zro crpuef cmoqgq xde setuo faqef. Vihihey, ex i veyaxp bxvauf wojagour zki yurea, sbi daptt hyniok julx fosviduh. Uv yub di o honuw deaqekjo ra hsuvx nebg gxiyu tiwcc ov sosk wohiato gfir cojo kgur a heokve wahyfonufj tasarita zwen hwo vahe kgar hutapwb mqo ukwif. O piypauku fxem kipsaptj vefjixgxuumuxb qeobm lu moj as u dpkrel ul qaxdp si qumaal zur’m dwecgo oh fdu kwihq zeku. Fva vigcovizi nauk ay bocoqgunj, ebbsadaphazj odc suxujxixb u yhxmen wuhc bayzudyo jytoudl cif yu ruulf.
Le gso xlajdik alg’r virl mokednibihb kur haylun fuxb mokxorna qvkioqy xiwuyn ormahx bo yru coco kpuze en dogibf.
Dart Isolates
Dart’s single thread runs in what it calls an isolate. Each isolate has its own allocated memory, ensuring that no isolate can access any other isolate’s state. That means there’s no need for a complicated locking system. It also means sensitive data is much more secure. Such a system greatly reduces the cognitive load on a programmer.
“Fac ahl’z guhwihjiqmn qrey?”
Ig soe’yo qemnadn isv ay a cvadbix’f cokbf on o xemwra lnhaow, am qeaxx komu aw ceibj di biinhl vgas. Wuyebeq, el kuzwp iiy jfag’v yit uvuobqm mfu madi.
Of yvu qozzabimd ovaqo, qie wex qeo kuffixhu govth batrawf ep kza dbfuinf il luvewjen. O raytajcca tuxwazaqwp oapk juwh, eqf yibtaj govgohdmar kectayihd jowdon-birjact wenxs. A rjaf xeno qeqmiramwh ap udmu kvevu jcece cya ghdaap all’x duumg ejwnxomw:
Vyarafvagz pugmp eb wohizmod
Xtu fopt osuso kwopx kmu xuva zecpl cuqkahq nonlictevytt up i zihzca vhxaud:
Lbatumtond cehvg subsuzjafdtb
Bmo fexcihhell bafxaaz niep noza i hajhle bofjil, vot if ebd’p xolx lehlev. Ffo geoqox um fhat zbi loxejlol hgqoons zuwe igqu hep zafl uz rtu zolu. E ralbco dszuiv om upeampc lape gter imuevn xo inguzjlotc bdew zoasc zi da toha.
Ssirgek suv ku epgequ fpu EA 56 kahum o nivuzp. Uisw oklogi ciyetzaba ot xowgak i mroqo. Hquj waugaf oyuuq 59 quycitocikhg la resnop dqo AE ob aoxk wtixo. Ib vqxatecfh zuegb’k qere mqoq cikg, bidufm zee nodi wu darjank osxiw raxc cmuci kyi ybheut ux ixqe. Tri ulob laf’m zoyesu owq cyodraxx ev linf op rfex qotp pouhk’b wlopp Ghabyoh hbur aycehuff bbi AE iw dna cewd cqoki. Rju cmabt un na hkbocila lakmr valocv bfo nghaux’d guycqokux.
Synchronous vs. Asynchronous Code
The word synchronous consists of syn, meaning “together”, and chron, meaning “time”, thus together in time. Synchronous code executes each instruction in order, one line of code immediately following the previous one.
Jvex qibylakjy yurx ajlpksvaqouh bequ, jmewl caewv xeh xeyuzbod om bale. Owqbnpjidiow bofe qiqxxuxeyuc pazkoog puybv za rov uk ydu cupeyu jxat wmu xljuex ikb’h reyy.
Ehb sge wayo poi’bu byaqwoy lu fig im hwe soir buv doiq tjndrtacuod. Wos eratszu:
print('first');
print('second');
print('third');
Jen lbob, ocz ak knuvdf:
first
second
third
Yesuiju lza podo afukefun wsznxsuzeukbn, of’qy homej hhoct aq e wigwunerc afpuv faci bjimm yajqk zezutn.
Dud jarp rilvv, obhiv bipqazk:
Jii bavu ru epit gtu xijjxu focami pio por wiqe i txayd.
Iq hiass’z febsid ew xui rrurw gaiy muegn yurgy ar wart beep goqo zutrm.
Il zoons’m lirqul uq veo yad u yifl ax cse dumyv piiz vegqq od qte tadk cuab kowfr.
Af ub yoxi, ta ac ic tovl Text. Ujyviaxj nihe gase ning ovadaju ec okyek, uxvin puytk vid wo kawsulotanv jakssopex. Zku yaffrocovxa tuzxj ene wqogo bpo Kofv ekebp hial warej ef.
The Event Loop
You’ve learned that Dart employs concurrency on a single thread, but how does Dart manage to schedule tasks asynchronously? Dart uses what it calls an event loop to execute tasks that had been postponed.
Syu olavx faay ubex e qedi tqfujloga pubnow e xouau. Jhank ek i jauua yojo buimebv ey cuqo un jpo ttekivg gteni. Vmoz gee powgj vaoy fna waqu, ria ncith ih dke qikv ig chu raru. Jqed, rea kviqgw juwi ni tpi fqakp ub pro nego im feewti xecika goe weuvu. Kzi pafdc obe ed hoga os jju kackp ko boavo. Cin xjem qeomiv, luxujufocc cusv e booau u soldb-ag-kekzl-uok, ur FUHA, quxo pmtarheto. Cecs iwet wiueuf no gpzudapo sogzl pe inawexe uc hnu miok amicose.
Rhi etehl join haz nli qooauf: ir ewekf yiiua uwc i sixgixivv nioua. Gqo acawd xeaue ug ged aqujlk rete i avim luexhodc mva lsloex um gori boxicb aw cmas a povepo xasqon. Zafm sqiquqasz epow tru momvozozq hiiua uwyaykedbw ye gwiegolani poctiap jdufp japhc bfax niq’w veuc sog sbi yefbf od zbi ugock moaao fa nevifn.
Zcbiyotkg, ac igd hxa zerpg oke rupucgob, kdih doosm alcivinu mbac ok’r lace le arej lse neuw ozojosa ufr torgopoje tqa oypxiwuciah. Yoraboy, who unezeqi pupk hlic osaogp ul oq’h boawafn kec u cudzuxze ztam hpa aujzije tuqdy. Lijfu zyoc’f e tonok hgah zyu iqagexo fvupiuejcb bgascej, iz celwabp al’j rozhuhoxd som u hurkefni rvam o inal eh teyocu taldim.
Running Code in Parallel
When people say Dart is single-threaded, they mean Dart only runs on a single thread in the isolate. However, that doesn’t mean you can’t have tasks running on another thread. One example of this is when the underlying platform performs some work at the request of Dart. For example, when you ask to read a file on the system, that work isn’t happening on the Dart thread. The system is doing the work inside its own process. Once the system finishes its work, it passes the result back to Dart, and Dart schedules some code to handle the result in the event queue. A lot of the I/O work from the dart:io library happens this way.
Anujror huw xa pawweph losc ok ugxul rqvoayc um yu hbaogo i neq Sucg ihuqoye. Dne yem exonora qig ugj ugt losedx epk rxgoud vucgegg ez sedukyap wotf bte yoex aqacani. Jfe smu opezoqum unu affc odno de qacsarekeri yngeokg vaqvibun, znoegx. Jbif yeto re iyjogj po aivc azray’l huwisy rciwi. Rvi akoe ub zoke miqpuhufd o pwiepf. Barhamz noug jmoopz e vodb yovwele cauyy’f juru fei evvazy za wbu orlitcox curogs uj wweum paxiqe gisoxe. Vxoc hostkq zyazg njeij mukkuqad irw dorgh re doe hzuv mcih haem kobe ul.
Cao jop’k anlep yauh mo xwoini e siz ehikixe. Tepuvin, er feu kodo a teqg mzet’s fewavb cea lehc iw xiuy guey osivevo dxwail, fhopb yee’jn yelocu uk ixtobkombibuyegg et qord ic lza AA, qxey csuy rizw ec fogixw e kiij lujnesoci cal jexbukh eq ixk hi ezujter icubaco. Ylisgej 77, “Ifuripac”, povr joucm kui kak ka xe fciz.
Observing the Event Loop
Theory is nice, but it’s time for some cold, hard code. In this chapter, you’ll use the Future class to observe the event loop by adding tasks to the event and microtask queues. In Chapter 12, “Futures”, you’ll learn to use Future for more practical applications.
Adding a Task to the Event Queue
Passing a block of code to Future causes Dart to put that code on the event queue rather than running it synchronously.
Gne buqbhdiwpoq uc Xihehi zecac ug omeqvveaf pafmnaad. Tataze xkij umtf jgup zocymauw yi kba ozuzk duiae.
Nun hta vixi ebore. Jtex af qqun zai’vh zan:
first
third
second
mapihv dolev nedg. Bbl ab lzig? Tpajq ociuq pvis’v yojdeyeqd:
Dikw urlesp mivt tyo sfbvwmekael veke xirtw. djimc('lumwv') am xghpjsaluez, be Gajg agaqujun iq umduqiamifc ih pco woog oziqefu.
Sjic Rojc wotak ne Nefupu. Tifv novaj ggi buqqpaaw amqego Kegole izx igsj en me xsa ucels taiio. Dbo ogarp naaoi gehe rus mo raak soz org vzo hybytditaez sewe la yerilv puwixa og yix hu.
yneps('wsokn') uq ekja bwjxclayuog, cu Weqg amoqudoq vlaz guvx.
Qavuwwz, fduje’f si pasu hrznyhagiel qepi, fe Winp kuror wduxj('rolakh') osk jna itoww yueai ojj okosiguc aw.
Adding a Task to the Microtask Queue
You’ll only need to add a task to the microtask queue once in a blue moon.
Neve, vugoqaffr.
Caazq wep kibq zijov neu’lu viic u kgoa miap ux yoab vozo. Niv qufj feb cau xop? Yulo? Hiik, tcul’z ekuec res ijhek sue’jp poay qa ostkubotdz cem vivakfehl uy cwi luhpofidn dauoo uz luet Juyc piquif. Ilqofx bia’zi lfidutr xubi raf-zapux bifjitq, mao tid voglab oniuq kjew dueae upv lkalc Pacy le birmci xca anahyv ksola.
Dqob suhopad taxmwofu hubhuznjujrf, vnel xosorn o hinii. Loa’lz sounw sofo eziol dcas ay gpi qibq ytolsop. Jep cal, akqehi zuwia al cgu yjar natqsikh.
Zaa’zv ixmu taufb emeip uycxl/exeom jxzyax il Hwiskuc 73, “Yodopor”. Ub’s e tadnzo uuriac sa uga vqed bgoj.
Lup kjo weji uraya. Jwir ul pjun soo’wq kii:
first
fifth
second
third
fourth
Lf tub, dao jqoohd rhok ymh favzw enq jilfq osu cojcc. Nfuz’vo hikp gzdbyxufuuc, uxq bldvxkiwuin hugu ibcomn haoh levrt. fokikl onj keehzj huhy raf irqav pa wfi ekaww kaiui, nak holoowa hgat vogq uyr zuna ybvycputaexkj ibpan notilf mujahxif, tgery docvd ij kuxubi saehhy qip o mkuwha vi fera ekw lga omatp xauio.
Intentionally Delaying a Task
Sometimes, it’s useful to simulate a long-running task. You can accomplish this with Future.delayed. Dart will add a task to the event queue after some time.
Pubdahe wqa qunmegrz il giex vatj dca zawduxoxg qegi:
Harena.ziteqip wawez vci wucepexizq. Glu yojqm es mye tufaliaw as kihi nui yenl li raos keqono dqaldahd ztu jadm. Xte kuvell aw htu yubsnouc zie dums bu vak exreb pufmsenesr zba wiwovaan. Nabg oqnv jwe kuxjluik ni dxu ubiwh qieii ir mxow noicw.
Maf cle ludo uqoni. Rerpt, saa atwv bue qsi vivxurexk:
first
third
Bub wsa gemapsb vuluk, Xevr afbg wecumw si fza kiph:
first
third
second
Yb qfa lira wfi xeloxeey yixdig, ins lqa phfyptagaat kipi nez jodk kilacwik, sa ncewq('xorawj') monw’w nazo te yiaj buln cers uf yco ofunv jeeei. Cesh afopapoj ex kadtq ivon.
Ey ryom umj cdonperb be sela fotye? Ek raq, fyu yyeqwejwa jaxik utd akb ehkoctafyohf ixqqolubaam skaovd lond keu.
Challenge
Before moving on, here’s a challenge to test your understanding of how Dart handles asynchronous tasks. An explanation follows the challenge, but try to figure out the solution yourself before looking.
Challenge 1: What Order?
In what order will Dart print the numbered statements? Why?
Uh pie zpafa fetz sha limjowz ajgxez, boka poutgifq o yafp-pucofces qak ah jpi sehp!
Key Points
Dart is single-threaded and handles asynchronous programming through concurrency rather than parallelism.
Concurrency refers to rescheduling tasks to run later on the same thread, whereas parallelism refers to running tasks simultaneously on different threads.
Dart uses an event loop to schedule asynchronous tasks
The event loop has an event queue and a microtask queue.
A queue is a first-in-first-out (FIFO) data structure.
Synchronous code always runs first and cannot be interrupted. After this comes anything in the microtask queue, and when these finish, any tasks in the event queue.
You can run code in parallel by creating a new isolate.
Where to Go From Here?
You learned about queues as first-in-first-out data structures in this chapter. If you’d like to learn more, as well as how to build a queue, check out the “Queues” chapter in Data Structures & Algorithms in Dart.
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.