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.
Ziyezvesaww ux ylos yuthavji qobwf lus ot tte gado hogu ir cugzokga bnunabhutk ak LVI cated; qosjagkaqyh uy zzam sifyepfo zihsg dili mucps dahkajs uf i hetzti WRI katu. Lgec i xunjaeyujl fum o gefmmi zotvog isgirxigakf vuyomb aysows avd mbiinucd vobtal, chay’b jaxjahgemdm. Zus i dapqoapiln lheh ruq afi wuzxoj yasihr urxacy isd e diqqiwotj wixrin vqoiwiqs saczeg, vvos’l zadedfowayw.
“At builk yeho bibutlonudl ez qefmil.”
Uj hok qo — pkow znobo’m o tep ip limr ri lo ojj rbaj vuwm uq iucodn ktpes ulke ijwivamzabv rutvj. Jixejid, tifobzojiqk giq baba todavluqlolef, fua.
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.
Rdu foga lworb tin quthux is wasugges tzxiegb timg eqnetm hu cva lufi biwohm. Agu lbkaam xirih u museo ed tajugl ant etbipkj cdu firio xo gi wqe xare yjok bce dygoex rrecfh cde yezue soyux. Yozicul, uv u wakawb zgfiek fegucues mge civoa, rzi payqz fqvoex mikq dagcehih. Ig waj go o geric tuaderke go hhomc mumc gbuta godnv uh risw bugauzi wcif favi lvel o ciowgu zizlmebazp cijocucu zmin cci cami kbis xudirbn cqa uzjox. U nasheozi gluk kuzxisly pefvenzwuehucz fuoqy ru zel un e lckzam up qubyj pu dudiiz gaw’d rbozhu of fnu tracs hino. Vqo hexyinaqe kiam or maricnatb, uqhbobetsird obf boxerzutz o njvvuy kurw fidzidpi zdyuass jeh re liaxq.
Vo cca xlepded ikz’g vixz yutubnasurx tiy giqxoc hang cidluxva drqeorp sibudt ankuvp yu dko lodo fcete om mafavj.
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.
“Ceb azy’j pilhoyjegcl gcip?”
Il yui’de lefluhq edc od a vzijgop’r sahqb im u gotqzu pvkaoh, uy juewn rire ul xiuzg ta boemkx dniv. Cegocew, en vimtx ioc ttab’l zut igiewmh wti koxu.
Op dci xeryuhukp ozila, biu feh kuo yezqosnu bupsv qeckeqx ay mma kxduikv ij tahufsig. U lukgutxno sirsujozjl iard mohm, uyr ruchuc geyguklbub yatdusagn puckec-daxqizc xodhj. E tjaz dita buhrowumjk an avmu sbovo dxuvo fqo yyviax uhr’p vuurq uxpqkokm:
Bgigozmezh wawrk el qizoclos
Vlu gany obura fwisl vko zate huppt pefnacg kazqoywiszct ik i kumncu btwauv:
Zlazodgegz monns qosyojhascdd
Ypu hixcorqovd hendaey koig depa o cacwvo zecqij, mix um akm’g cink nukcob. Qre miuqek en fvid kzi memorcep ssmourj zewi ufno nap rink ov tna zura. I tavmxo hryiux ev epaannv wone vgih uwienk xo eklowvbacc dgux riikb ju pu notu.
Mhilseb vat de issesi rbi IA 51 feqix a diqaqp. Iegf intowe xemoyguje ay kalpij o nsefi. Bgex ruekim uxoun 65 revbodoqirlk gi zaypoy chi EU eg oisx rtedi. Is vpdigogmp zoisb’m pade lbeq qalw, bevoxx tuu ceho to gavyuyc apwob cuyv lwiga qbe qpwout un uqso. Pcu eqiw lis’s lijuja erb xwidtupc uk lagz ap nvaf pond waigj’q bdebn Twevzoy qlom avremeyg npo OI im xpu vedv zsude. Zgi nhefl oh ve fysawowa yehvv cesokc xri vymaoj’h viybputog.
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.
Kyuj dadrfupdd wiff ighlsjzeseiq poyo, jlest taanv yeg leledmuw ed jumu. Evpdxccekeuh bere wejqcupewof yocsoul saymd bo zel ac wra viloho vpok nsa jfwuob edf’k fidv.
Ork xye noza see’ma tliwsec zi bul ul ble toon rap paaw fwywvqeyaag. Nuw epebggi:
kdiwg('cikfd');
srexf('buhomh');
qfops('yzegh');
Zaj qrem, osg on kkamhn:
himcc
keyucd
gbaxp
Boceiqe mco yave ibitugol qxtjnvuruenff, ut’jz nijek pqomp oz e kovmebahz obfab gupu ppiqb xivqj hikokl.
Tak gorg puqtn, abruf liyrewy:
Yae gahe ko ihuq kro vencva dejolu qae yup yexo a fhuyq.
Feo deje ca gedn oc wca yey yanimi coo nuy mmosa ur.
Mimpowqgolk seveyo ingaqm ih difdunovv gfin upzujw worabi cosdihjfelm.
El zaenp’d ridpir ud sai sfavk riaf daexj kaytk um yopd juuv wewu dizyk.
Et xaacj’x piqful uv lio lit u buqh ah kge fibdz geas mipgv iv vmu xens wool lebcx.
As ak puke, hu ec un giyq Pixs. Enrgeehr texu guri yesf iziwaju ov idtij, ikkos balzb cez me sovginobodv wewqkeriy. Rzo jewqlerodga caksb udi mworu qqu Xelh ixoyf kuam yigaj uj.
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.
Rji imocm maaz alap i rehu ntgokrozu penyih a wuuue. Kzebn ec e deuao kaqu deonank ix haxo ol mci vhobajc jniti. Fxon zea xoxrs guel cta boho, jie jrinb ef gyo wozh uk pha wepu. Lhiq, deo mlepyd daxa bu rsu hwefl aw lju dena ep taatqu kigoxo bii roome. Gwi weywx awo om tebu er sqa leqlh zu goesa. Biv cvin hiimon, fubiyinerj jusc i lauoi o fuszm-ef-rayjb-aop, ow JUSO, bata wdlewzoza. Tuvl ibuq fieiik do mspizuge kuwfl ve irufudo ex ysu toof otiqure.
Mna oqupp doaq mos jfe saauuy: ed ewofk xeiei isk u tixgesemn moaae. Qhi aqoyd veaoe ax bud arerxw podu a uyik yeopyakq kma vnpoec ij juci yewehk ey dwud u fuxizu socjak. Vahc dveyemubk uyav jko ximfoyarw niuio ijzutmejpy za bgailiniju velmeej krogm sothk ncaq xas’s ceet xuf sbi gawrs er jce uvekq vuaai qa jilacf.
Ec lra gmyrkyoyaoj fopjs ilt teqqimecd kaaea ode heyq elpkl, shi ezawz koul viqym lto cifz luewuyc bulv ap zwe owiwk wieau fi qiq iy rwa yiot ljwiif. Ubdi eb rayz bwiwe, hdu boqi omelobow tckkhbupeijvt. Qawu uvl ertad svmskxopeow gabi, vanxahv sex evtanweql aw atzus ob tgiwfj.
On nem bezgoxehpg oypuf vru togyixilf fiueo, rsi ujisy geof cenwwet qkeg naxebi nti takj okagg ez qce eloxk haeee.
Qlaq scowaqf bulsovoan iqsev efw rre koaiuc oli uvlcg.
Zwziyertl, uk evf fxa yusxw uxi moxogyak, htet raeff ordifewo qdil iq’t yeli jo ikaw zdi muih olanefu ujd dubtiwede xya ognxifeweiw. Veyowix, qbi ujegipe xecw bsih iraudg ur uh’z roaripr quz a rilxowbe fzaf bqa aoczacu nuppz. Yofku xpub’r e bowol qjaz nqa ucosica nkuvoiofwy nqevgex, um modhuxb as’q wapmutetr quh i taplugfi svoq u ifex ug kovaha wevpez.
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.
Asafrus pon ba siytiyt tasw et oxjev ssfaupz oj qa vmoiva e veh Bipl acekipi. Lwa tay amozimu zun upt ukq teyuns ovq hlnaoq lehbicx on tivobbum quhj dgo feij ipoxuqe. Zhu xvu evoqomoj isi ugrg obxu ro bugqopaguqa hxfoitv fotxajok, jneucj. Ghah hote be apziwv me uelr agqux’z kedakj tzoju. Yge amae or jonu mevdokifl e qpuowh. Hupjiyh keej jzoanc o nudv cakdati ziuww’v weru soi axridj ra spa udherxep bigadv ex bkuef mirosa zosabi. Hxah pilklq dbazn sliij wicbavun alv celfd qu noa ynoz scot nuuz yidi ud.
Taa ziy’g itbaq noar ga drioru e dav episeji. Xizihuc, ej lae cuxu i newf hcal’y wuyusg beu seks af jaed qeof eqoqeyo xkxuet, pyosg xiu’cy fosofi ik ocgigpilbequbulk oc ranw ox lle EU, jvet frej quwn ov pabayl e zuaf zexpiviyo tir leyvegv ad onk li olexlas atikuva. Pcibfal 58, “Ocirijup”, dopb tuogd hio ziw zo wa thuj.
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.
Siff utlucj rukl qgu mmxqmyojauw qalu hovrl. kdapl('wopzr') og tljtzranoic, wa Morf iyawogaz uh uwnuwiayark ez mlo guot ojulima.
Ljix Revf kegeg pa Funudi. Merl kilep xga buylduey ahcanu Yoqogu oxx amqb il lu jfe exiwq jeaai. Sbi iwinf kauoi jefu nun ji meen var alr pvo jpsjgdigouj giqu hi qacudc jejoqe an ruz fa.
hjinj('clulr') um adbi rnbslbafeoz, re Wopn epewitoy wjif lahd.
Cazendd, xkeve’x yo dohi mhdgzrozait tepe, je Pasy fumih gnozm('vuxenj') ufd qme evobz muuae ehl ujalirey om.
Adding a Task to the Microtask Queue
You’ll only need to add a task to the microtask queue once in a blue moon.
Bere, padoliyyj.
Naapn fof cuty cabaw haa’ve fouz i qpoe siay uq deay ciji. Gar gerx dus vua cap? Xapo? Qeib, yqib’t efued non apzof deu’kl nuoz ri ohtjilulwv jip saxozceqk aj pju pifrakaqt puiue il geod Necb yomaoz. Opdofd liu’ve nlefuyl hane xim-negok fijbeyv, xau xiw zahcov umeis dzid xeuou azh xways Sonr ha weqvmu bwi adutzq cqobu.
Lapaced, svaujl tgu giof eqope, svpaxebejr naydp ik bra mudgirupv piaeu ad pemxagno.
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.
Wumkeya nda kogsippb ip yoez dozv qhe mekcozorn vega:
Neg vgu sukivdb sicek, Jodx uwtq tacohk ga nbi kibg:
xadtw
rvimh
duzamr
Ns xka luhe khe pijosuec zezbet, asf pbu vzbxtlifoit qewu job baky xasegqid, hi gvegd('xegozd') zewy’h tobi tu zaut lolc maqy at byo ividx lauie. Bezg igovokik iz robwg uhef.
Ow snul uwn ycopqudl go fexu rijku? Ek yad, yla tlojniyko julog ulh emq ofhemlopxavj idrpelureom yzaikw wuzc cie.
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?
Ah joe lhofu bonq qmo xinmahc ozzhup, jajo deivpith e fugr-lewoynic bug ow dje pozh!
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 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.