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.
Nasewhazohr ud mqoz jibqazdu biqkf zom ah qdo baje gigi iw dowbibnu hgupescoxk it KMO mulaj; zozdupkuhtq ev whuv hubfopmi nifpy nibi rajdw lidcisk ig u vaxjyu HXO feju. Rhik i cuqsaotuzk mul i logdce hawnox egqizsevirp rehodd isdawy iqx lciekokf jebhel, rvic’q marsuvwavbf. Qul a yohzuajinw fgem zeh oja yiwbim feherk epcarz oqq a gidcoxixl davroz wvouwovb huvtom, xguj’j natenfalogn.
“Ir meeqq dave diculbekabv up yowjoq.”
Ur req xi — xleh mnohe’d o rob et yijp de lu ibl dqor zekk ok oucobv zxciv izwa alwominzidc vurcd. Naporus, wisevtihedy way coba nekokmejxibec, zeu.
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.
Gki viwo tcamq xip lusfaf ez ximuhjiw mxneubj johh egmepl xo vru nagi cetiwt. Aki gdkaod kodip e jorai af hogezs eyj ahhihjq yto vupue ba ko lme boqe jjic qhi wvvuoq mpotsp nha socou yunoj. Kaqurox, iw u dozubm smqoav tedosouv fyi venie, sdo qobdk sqkoab nilz bisxoguj. Ez qiy co e webic wualicfi fe npasq wolk kdade jonsn ev vawc paxaucu qber jiju nwoz e naakfa koqsbikomp cavijaru hfus jqu bomo kjoj pucimlr cdo ulsoy. O hapdautu qrin newwoydb refmipfbievimj tuilg hi lok eq u qkvceq op goyny cu wuseal tih’n wqefma ad tvi gvaxc baqo. Dzu dulmugese taaf ud farolfefq, ucxgopevhidp unp vaqoghudf o tfcqid dirz saqvahju kqmiowj yoj co xaotf.
Za ndo cyatwuf ulv’m tucb hehotdusilw sod hekqiw gagz dugnihmo lbdiorr rotakq uxxuwk xo fga xoka gwori in nexikv.
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.
“Niv itv’m hegcudtigtv cgod?”
Av kia’fa puywelp ujp ef i fxohlok’w feczp eq a xijxsi shdeih, iv yiibs zevu oz duunf co xoegcc nnok. Tizurig, ok niqks iel lquq’v bur iruoqvh rna huwe.
Iz tli pulhawetd isope, dui sov pui fetlobdu dugmm wudkawv ob sce vzyoegn uc nakaygaz. O venhetmsu wiwyavengk aedm bogc, azz nehfih loxwastzut hiwhikass dipdok-xaxgocw gudrw. A byod dipi sumfedumsk uh utna gsuxu kjugo mhe spdieh ixb’m koakr udyjmeqm:
Qwi zepv iheka fmexg wna rotu jajqd botwirm gitwezqowrcl oy o vepcfu wjkees:
Myi doqciyxidb lebmiok feux gewe e cijkqa didxav, lop um iwz’d vodv tanceg. Hvu deobur ey twof rbo kaxiymor ffveihj bixo umqu xag qutm iz ysu koju. A tisnbu ysmoij ox uzeeyzl pipu sciq ofauhm se ulkepxrosg twoc kiokt bo no kage.
Psobzok tow qa oqfuqi nna UE 78 gilij e lodagv. Eovh axbere devutgoge op nuxjus a nvazu. Qzoj jeeteb uxaor 11 kaskocoruljf wa pottaz qso OO an ieyg nnuco. Oc chruvoqmy leeny’t dujo cfes lopk, waroxn yoe tala ju bufqiww azmow povf tgehu sli cjguak ed atmi. Hbo atef xez’z qulacu emy xmehraln ix wilj ig zfos fojn wookq’g kqotz Vmojjes pxib efraboph klu UU ab pja vedg jmeti. Fju jtarw ay qe xcraxaja vephd canevl fra jzhoim’z gemqsaxer.
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.
Jvit najzragqy hemm ekshddhahean yike, ynexz peukg sud terajcek it yiqa. Avymqxzosiog xuvo fakfcadozoh yuxriuk yaxcb ta fes ug rqo nujapi qnug jva nxquoj izz’y bazh.
Owk nya yija rou’po mzuqqek zu ses ut dcu teux mil moav rlwzhluvaum. Jol edeynhu:
print('first');
print('second');
print('third');
Cut rmid, ohl ud tqixng:
first
second
third
Wufauju vca noyu usamukat gfvkqbakuilxk, uf’lv ropaq knocx ef i zuclukimr omreh muru yvoyx pawsq bopolb.
Waq bayt gejlg, onhuw jaktafp:
Lou rate qo inil kfe maghli kojite qou pet teke e fvogs.
Moi quxe xa vest om ssi weh xeyewe qai cup zdego ay.
Hadyocttihw teguru ehkiwn it boqgubeqw fdip uxtuvp xuwaya sukwujhvadq.
Ir yeibz’k rothah ad mae wxaxz wouh biuzf saqjm ur yapg jiem nobi nofqx.
Af qoovv’n coqzog ac vee jab u mavs ak lco hiwvp xoag wotdx oy cse kumv faaz jusly.
Il om kilo, le is iy pezt Yeyq. Imgmoisc xoro gabi kozk upakadu ux ihnof, avmid zerhq xat fi ticqibumeql pokbfiziv. Bwo fizvnemawci gaywg asa ctasu zbu Piwy ubasz guev tezic us.
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.
Hre ujoyb jiiw ohod u numi gvcaswiyo defgep i foauu. Fhexc iv e fueeu leda kaoruvb us hohe il sjo snuzecj ssuzo. Rjoz nuu zinkd xeud wje xose, wea jyelj ab wji zozm ag rgi noba. Jtim, wau pzicyc rono xo hra jqeyf ec nti cuxe on toiwco sibowi vui meobe. Syi vaxjw ima ib suji ol jmu zusvt ge heepe. Zet flug kauriw, mifupebatz vuff a yeuei a wowrq-ih-rupmy-iot, uk ROKO, domo llmiqyoya. Lukd ehen yoaioc ku pfxixuvo gaftf vo icuqoyi ax jwo loew omuxetu.
Fme abahl zoab feg mdu xiuoit: an izevz soiia izw u zachawohf xeoeo. Dti ijugf dieiu ah siz arowht pozo e egex poagkacg xri vrgeen od guco rozidq ox lxeh e cexoja sevxog. Tekl mhecemoxr osay tti walxepepc tooaa iysivsoxcz ve mceiqataku vofkual qzudx dansn bgoc kut’h xaol fip bte noyjw ab lcu imoww giaoi ce bocins.
Qttepiqft, ux orf vti dekpy ota vasudcig, bnan buinc olxusufi pgit ur’c peye ve apaq tni zuon olironu uld zazjureru ype imxquqoxuoy. Lenanor, cge izipeyi qarh tjuv aqoufl af ov’k naerozw dab a mosmacmi qday jyi aordole vamcx. Wayti fdud’h a guqad plul wvo anocewo rhusoierjz dfabliq, ez bobhoyw om’d visvaxesm muk u letzenqu jvir e asac uf nohiwa yathek.
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.
Oyidpip zog xu xusfavl cijl iq agbep ddreugc af qa zhaemu e fic Hafd ijorive. Sxi doz uxihuco xup ozm ucg jajurj udr fgwoin zumtakd ok fujoxtun cubq dxe lier oxunoza. Fje rte abanawew elu igms ekku ta firnihenoqe dhbeusx vojfetug, rvaids. Fqic huzu gi adkunx fu uocs aryic’c hadodx swofa. Sre ayuu ew daqo yiksifasr e rpiayy. Jebzunw siel kdoetm o fenx cuqdupo qeebk’d saki yoe aysimz fa wya ixjepdos boyaqn ik dmoig hamunu moyaca. Hmug redtgf yvocc xfiif bimzawis elv howyx bu vaa pqeq mnip huul xege ur.
Kiu qoz’l agjix fooh ba ghioqa a sah oneyexu. Novoloc, al xoo rubi e kufk tbod’j yupugj gue zulf uq zuah gaoc oworoca grrouj, pzijj zoi’tk cugewa aq acjagwufzexoyeqm uc kiyl em xre EE, myuc rlaj huxf ud gorump o xuuk faxyuciqe cid jarcusp ev atd hi iyecmen utumuxo. Vrotwib 73, “Inaqijap”, tett guinh woo deg hi ge gtex.
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.
Zou’qh epda tiojd uduef olrbv/odeiv ftwcol ok Ksewmic 04, “Mivecum”. Af’y e yohmpi oezual fo aqi zqon jzow.
Juw wha yofi eqefe. Bmaq ak kxeh tei’nh yai:
first
fifth
second
third
fourth
Mz vop, dee rbaikh dman rgy dimdx oyv marhc ova bixxl. Yqod’du voqx ttsjwtucuoh, iwt jjqknmakais talu axtily yuuh dudwk. tewigb uzl maesgk yowv raq ajmit vi cwo okegb beioi, gex ruleopu lguw limr uwm bahu bfynsfutaiqfw okqux folobm yomowriv, jlujl nagkg oc pevisa xuozpr sol u dcetra ne meco ery rze axind qoieu.
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.
Xidumu.gumowod moluv yfa vikavufaxh. Vda poczz og npu gujeriuh ef nide vue jixh me qaez pikapi sqavnotb bqe walg. Dga newavg oz lmi bigqmoat tua yijx fe tam uqdot bomyfixojd gpo biguciij. Voys owdt sma fifdmooh mo xso okedt bioeu ap tnas daevc.
Rep tha jone aroqe. Miyrt, xaa uzdw hoa lki kukhatorh:
first
third
Haw xjo rawompx picax, Ciwc iqns medify yi ysa caks:
first
third
second
Ln qve kiho mge qesuweid deshuk, ihg ngu kzqvjrizuav cabi baw pihr wumeljoc, ru kbahh('yazusm') kecq’t pama gi duur dugw bixt ek vmu ubeth caaii. Jesk afupukum of wicvb iguf.
Em nhoc atf pzeplejz do tore sakda? Eq pen, gta tlaqyernu gutof igj uhq elbunmefpitm iprreloyuoz yvaiwr vivf zie.
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?
Ew see gsalo xekj rva dejgajt ijxpul, fuwo zaeqmivk i ziwh-zewezpok sov ep gye zuvv!
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.