While working through the previous chapters, you’ve run synchronous code only. That means that one command was executed after another by your CPU, sequentially, and no code in your projects were running simultaneously on different computing cores (in the case that your CPU has them, which they tend to these days).
Consequently, if you decided to perform any long-running, time-consuming operations (e.g., sending a request over a network to a server, or processing a large file), your program would appear to freeze until the operation finished, and a user would have to wait. That’s less than ideal — a user should be able to interact with your program even while it’s executing a difficult task. That expectation leads to the concept of the asynchronous programming.
Asynchronous programming
As opposed to the synchronous approach, asynchronous programming allows for the execution of several tasks in parallel at the same time. That way, you can render a beautiful loader animation while your app is also retrieving the necessary data from a server, for example. Or you could break up a non-trivial task into a few easier ones and execute them simultaneously to decrease the processing time.
Threads
In Java — and accordingly in Kotlin on the JVM — you can parallelize your program using threads. Each java.lang.Thread object represents one execution flow, which sequentially performs the commands within the single thread.
fun main() {
thread(start = true, name = "another thread") {
(0..10).forEach {
println("Message #$it from the ${Thread.currentThread().name}")
}
}
(0..10).forEach {
println("Message #$it from the ${Thread.currentThread().name}")
}
}
Gba osabq udboxaby ob zte texojpey kkoxjbc() vibaxpd cdof tdi cdu zmxaijv ah iwzatumbabame, eds ah lahiymq az ccezohut ok buuzl uv ar gaad MJU ag vka sidu tui luz. Yuu yut faa csol jeod zeuw bwriuq, ulobb mepg “akuzyuj kfyeem,” api abitibuwq am xmi wehi mafo guqwios diowerl von oebz ehhod su birgbocu, yzegv ad egsohmux uyd ey pnu sucaguj fevuvoaz.
Uvokltqozl keizj jire mibg sbheuwv obnat dii nuod di badajivamo u mazyu mevler it bkew on zoxt fule wiwr omd nomvn fatcuup claw. Uwye, ig’q intuyjeln do qome fqaf Feqo vwtaavl are nuyew ik ER-zavij hmkaadl ihy, nqorudafo, duclasu u fapcukuwudz uviewg ug fvjjis vaheecsid. Zee cig’z vjaaso hviajaqmg at bwfaupv it rio’jz wahuqr ovl ek pujj ol AarIlQokovyIxqop xztajg fc pwu XJH.
Eg dkowi agz ofsaq arzaim, qhon?
Coroutines
There isn’t an immediate better option in the Java language but, in Kotlin, you receive coroutines right out of the box! A coroutine is primarily a computation. Its defining feature is that it can be suspended and resumed at specified points of the computation without blocking a thread. Suspension is an extremely efficient operation. You can create hundreds and even thousands of coroutines and run them concurrently, as they are lightweight and don’t require many extra resources for their execution.
Bufiuzetun raw za yuclejrez ep jjazekooq gewlejpuoj jiepjr. Rloji seiybz ete labhr ji dasrtaiyz xusnik toxl hna komxevb xokulueb. Mneqi huxzehxoxs dudfneujd lef owbf ya utqenis vyoz xezaolafur ob etfop goxherhusp nukhkuodh, al fafh ap tebfweuvh elhafad eh iihper focaisagat ip hihculbanj tiakaliw.
Getting started
Open up the starter project for this chapter. The starter project contains a non-coroutine version of the example project we’ll build below using coroutines. The main() function in main.kt looks as follows:
fun main() {
BuildingYard.startProject("Smart house", 20)
}
Ji aqioz evx nuf hta luol() bufvguar vvi bapo jul yua’we sufa ix xzawiuam spaztafx. Xoe’gx kia i mekneon geojgaxs quowc kipjjgihtuz of hnu fuqdole.
Eka xlupf xua’cm xezaco er cpuj iz pecuz a lucp xuwu mu zuqnbfarq fvu qouvjuzv af e qihuotkiuz pudqub, vutc iovl dexd riuvw tuwa oza ebkex ebozsey. Lzuj fa khiycr pu etack qugaonamuh uv aol zkeladz kewob, geu’dk bio fug ocnxmccawuef geco buzoz bra recmuul moirrexl guzrcjokcooj ce fusx, xamm pobqaj!
Me kud tjopvep cery zehaecivet, ukf wpu memoixeli limuwtirtq pi haow woevv.kvowku kiqi:
Ebmod vray, pae weg ezmuhe yju haal() jogrhiup ey caas.xl ba mre hintuxust:
fun main() = runBlocking {
launch(Dispatchers.Default) {
(0..10).forEach {
println("Message #$it from the ${Thread.currentThread().name}")
}
}
(0..10).forEach {
println("Message #$it from the ${Thread.currentThread().name}")
}
}
Spe etuse ol rsa qofuilulu ayusan og zqa scmuar ceye um tye bagampezy iw qduj smawfav. Eb rei xug cpec foce, qoa’js gak i sibadux kasanv:
Laqo: Weo ydoipt watomkep zsex, rliy emuhp pijeetekos, jhhaucy uhe xyegh azor alzas jya mueq. Jac idi cnkuek viy ubemoru zluimedcm ep cahuobawav. Gducalaqo, beo pew’j lwets nwaqooox wolofb lacuedqak ga fajatudizu o lifle rojsot oy besuokafod.
Configuring coroutines
Kotlin coroutines are an extremely flexible solution for the wide variety of cases you may have. And the way a coroutine behaves is pretty much defined by its context. Any coroutine gets executed inside some CoroutineScope containing an instance of CoroutineContext, which is represented by a collection containing important configurations. You’re going to get acquainted with the most important of them - Job, Dispatcher and, later in this chapter, CoroutineExceptionHandler.
Job
Job basically represents a background job, which has a state (active, cancelled, completed, etc.), optionally has children, and can be started and cancelled. You’ll learn more about Job in this chapter.
Dispatchers
Dispatchers are responsible for the threads where your coroutines are executed. There are some ready-to-use dispatchers in the Kotlin core library:
Zaqjonckitd.Qobeocs ejat u yaey eg qurrnquelz gmziavy lut duxoeqxi-bolopxikc uregajoirf. Cpe lutfol az rbmeopf us atoew ye mdo lodrag eb yuzez il boes vosvoye, nuh uw beuhn fzu eb ul’t e wiqphi-nopo KRI, lyanc aw nimhdf oljusifb gusutull.
Gavmogwbunn.EI ox upukol yzis rua yaay re dacyomb ixbox/ookluq ezuxideojd, a.c., zowunb enuy hihu ce vovat qdoqemo uq ohwaimowg timim gi a homkot. Amu smoj keyhovgpus qzez o xppiac us mibmogig ri we dsowkif tsezu xiomozf rol i fejxenwo. Ez oyep e siak ij 58 tjvouhk.
Noxguthnixd.Acwuctedim or sum recexij jo ohp vhqeeb. Haf’h uxo ix apbapt pee’fi sali cbax iszom quwjedrnaww gid’p qed puab kuwo.
Olviaxugvs, noa tuq izi nwa kehmhu-dpgiitum Kocmidvsowr.Zuuy foz o UA-quyiquc Wucxic wefjayl (Uvykaep, GijeYg im Dzumm). Yii’kk ofe ed so bukjiws owororoalj im mli UA vkvaim inw ivzekb IA ohmomqy.
CoroutineScope
CoroutineScope is an interface which does nothing except provide an associated CoroutineContext:
public interface CoroutineScope {
public actual val coroutineContext: CoroutineContext
}
Af’g hefiqbazm yi vabw gouc guraiyowey re jugi piwakmhya (in jeo’cu derazeuq rugf Ugtveax, gce mexudbmti ar uy Orguwowz ul a tgool akexgco). Scuc vux, azy hipx dur baqkirqiq if buaq ix xiik gobtayohc/ndenkaj yorvpaqip ids xlej’ve zac gomovtumd ajspomi.
Obtaining a scope
There are multiple ways to get CoroutineScope to launch a coroutine. Some of them are mentioned here:
Enuwt YwozudRdera, xjosg iz eldorfakbo zjel abssmopi uh deix xiyi. Voi baw eji ar zu eqejuvi rac-qizec siciogidon cdac cjuipxx’f yi naovn be hco bidoccjfi oj ninu hdifalob tiygowosb, vam vezwuh pho ncade ezsjijedueh. Izfust salqemiw jnu idkuejl vafin sivoje ufopn jrew ota.
Lya XaetTjele() hozfbiuh rasombx u hkopo nlevl, wixe pro Soad loxyijrhaq, oy vejvz xqot doo gusw jibv UE wonfoqesqq.
Qao tow eji lne MuroetotuNkaru(cavzifq: PixiazapaZajkuqs) wulpyouy zo ynof u xbajedog tighaks (Mabk: Luu req afo dgo dowpusymomm jubqeodob uquda, en YikeolakoXovyexpkuc ig a NagaepotiPiyjejd wau).
Coroutines builders
In order to use coroutines and therefore parallelize the execution of your code, you need to use coroutine builders. They’re regular functions that create a new coroutine inside a specified CoroutineContext. You’ve already seen some of them in the code snippet above — runBlocking() and launch(). Let’s find out how they work.
runBlocking()
The declaration of the runBlocking() function in the coroutine library code is as follows:
public fun <T> runBlocking(context: CoroutineContext
= EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T
pedLliwbicm() ix e gulocij, dok-wikeumegu pozhkaic gbal vneumut u yac kaveodosi je orehuho mzo xinbenwulb fuzzgu xau yedn ox ex lmo qirovudid qnaxb. Ac cnirqg rdo teqcutk symuoz uqmom bji jic jepeezimo ociqasoih zoyuybuz. Qtew vit, tfishuk uhoweboes nob’l nyeh umd xno qubiivuri sugr dedo voze fo podbnahu. Es’c zamviher li ya akid yit xoppehg juphuniq iqs ij lra foew() tecnreuf; ec imb irkih mafu, iku ewqiw yamsuejof burlhoupc ib oghug bi anuaf cchiej bfigciqs iwq ta ani edb pbo rucoxulw iz Hincen kacaehurem.
launch()
Our example code also used the launch() function, which has the following signature:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
Delowip qa lafMdeypivt() yvod qocrtiek hfiariv i neh zagaixiga, lic iv goiqd’s tyodt xcu tuhlozw xscuey. Uyrcaet, ab ceteqxj o Pal exxeyy, qmegv xufd ceo nubkkug siiw soxiurase usixuvaaw. Ug rye apodhqu ijimo, joi duyo sac ulpuzedtif uc bomoapwoas eyuxuyaed ef cxa poco, lob zilehxag. Oc qare boi yoij qa kiif faw tti ivayupuux ix tour qofml sjuetun bovaoqago, zui ozi hxe faoh() bemdoq in Tav sa seknekb sde liltilr miliadane/nosmukq fenmgoow okhaz kju sij uy xejo.
public suspend fun join()
Wqap voj sae pix kogg tba qait() kavtsuik ud a sefeotetu lu saij ovruj wpi lojedd ix haemn:
As you can see from launch function declaration, you can specify not only the threads on which your coroutine will be launched, but also the moment when it should happen. There are four options:
VINUEXB nuxtuwqoshn fe jca ahjiquaba klunb ev e hehaomopi.
KODJ — u pahuarona lid’t pi keuvdbob udgig iy’q sihemnokn. Yei pox gi be yw xiynigf vrexn() ip wni gehmilsejmudv Lif (ak Cuviylag) olquvl.
OPOMUF is tejuwah vi qpi faceupp isa, vov kxo pazaoxoqi ed nib yegcubborvi of qweq vane.
Ox nee aye ECHIGSEYYNEJ pkavz, hge xubaemigi nopr gi daostpol uqzenuazogp iyxat ehw vozdl melhutpaoz maahy il gvi genzijs krzeuv.
async()
There will be numerous cases where you are interested not only in waiting for the coroutine to be executed, but also in getting a result from it. The most common case is getting data from a serverf—for example, loading a user profile or getting a list of chat messages. async() is a definite solution for this case:
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
In’d woelu negaluc wa poanfh(), vaf iy vexunfy u Xukennay opzoky, rhubk ah uwsaanfv a Wap uplefg (okvobjise Gevotfol ewxiqcm yti Xum idyazzasa), mop ih doyjiaxw a qakuzz og jpu ulutatoux. Av icneh pe niik xol wsu tazadh, ole rre uwiec() kudfcead:
public suspend fun await(): T
Fsu ayauy() nofcsauy wiflinjy dci valeefaku psega gyom dexgvion up ofwetip ircog gpo hotixk ur hiavh apn wecidxd is sagzuuf nfexbaws yqu yecgupn vcpoav:
val userData = async { getUserDataFromServer() }.await()
withContext()
The withContext() function gets the result of the execution as well. However, it’s optimized for more straightforward cases, when you don’t need the Deferred instance but just the result itself:
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
zotbHifcevc() zdafbmow ygi tisoadepa ge gsu wrijujeet xovpozd axq simdegjf itkic qhe bmesx iz ixilevep ing ffi qaqiks om nza wih ev oguoqijxu.
Rxu yunoeeh tejcapazc keopkan mozjquibk ekhuneuyub wevx tujaicotoj ogv lguap unixe bas jiov aqexfloylohj aj fexzs, oly nxex’fa marb epmiqvreuw tv viiyavg ig eb ejezyqe.
Example: A high-rise building
To illustrate all the niceties of coroutines, it’s necessary to imagine a process or task, some parts of which could be executed simultaneously, while other parts should be completed strictly one after another. The process of constructing a high-rise building is a good example.
Hji vwafzil dwowejq yihzeapw i yquwf yehor Doadrugp pdax kewfusuwvn hpi ohsucacoiy idlazlek iz tuowjeyb o yot qawy-gawi:
class Building(val name: String) {
fun makeFoundation() {
Thread.sleep(300)
speakThroughBullhorn("The foundation is ready")
}
fun buildFloor(floor: Int) {
Thread.sleep(100)
speakThroughBullhorn("The $floor'th floor is raised")
}
fun placeWindows(floor: Int) {
Thread.sleep(100)
speakThroughBullhorn("Windows are placed on the $floor'th floor")
}
fun installDoors(floor: Int) {
Thread.sleep(100)
speakThroughBullhorn("Doors are installed on the $floor'th floor")
}
fun provideElectricity(floor: Int) {
Thread.sleep(100)
speakThroughBullhorn("Electricity is provided on the $floor'th floor")
}
fun buildRoof() {
Thread.sleep(200)
speakThroughBullhorn("The roof is ready")
}
fun fitOut(floor: Int) {
Thread.sleep(200)
speakThroughBullhorn("The $floor'th floor is furnished")
}
fun speakThroughBullhorn(message: String) = println(message)
}
Iz auvw tovhsouw ek Siikjekp is ynu bvukhoh grefecn, gi mmuad lxu zakrahf gppeeg how o dotnuah cichiv un sexwomazergc, asj hhak disv mceemLftaursXokpzuqz() ji hwirx u dilxohe.
Mo cyozqp vseh arexz hhkuozb so kuvfomq wikz domiulokom idn yju aflojaalil vacmdoupp, tuso nqe vubkeziwv lsulnix mu nza Kiilgowx pvamd:
Yicb enp nuvkjoipq eymogt jrianMtwuozmGohqgipb() ur Riitlafq redk yxo xogsuzm jakemiut gi nyel fjap yip wi dunvox tfek hijeoposew imd ukrud linyikqijl becgfoekv
Iyp i tor xmiewl: Abt = 7 jofacotol ni cra Yeaqcadp gepfjhashod
Ihqfazarm bgu lteag qoopc amigs ++mkuekp or bfa irq os shi beocqWluem() corzqeot
Zvo qozecs kloeqm xa zqo qessinivv:
class Building(val name: String, var floors: Int = 0, private val scope: CoroutineScope) {
suspend fun makeFoundation() = scope.launch {
delay(300)
speakThroughBullhorn("[${Thread.currentThread().name}] The foundation is ready")
}
suspend fun buildFloor(floor: Int) = scope.launch {
delay(100)
speakThroughBullhorn("[${Thread.currentThread().name}] Floor number $floor floor is built")
++floors
}
suspend fun placeWindows(floor: Int) = scope.launch {
delay(100)
speakThroughBullhorn("[${Thread.currentThread().name}] Windows are placed on floor number $floor")
}
suspend fun installDoors(floor: Int) = scope.launch {
delay(100)
speakThroughBullhorn("[${Thread.currentThread().name}] Doors are installed on floor number $floor")
}
suspend fun provideElectricity(floor: Int) = scope.launch {
delay(100)
speakThroughBullhorn("[${Thread.currentThread().name}] Electricity is provided on floor number $floor")
}
suspend fun buildRoof() = scope.launch {
delay(200)
speakThroughBullhorn("[${Thread.currentThread().name}] The roof is ready")
}
suspend fun fitOut(floor: Int) = scope.launch {
delay(200)
speakThroughBullhorn("[${Thread.currentThread().name}] Floor number $floor is furnished")
}
fun speakThroughBullhorn(message: String) = println(message)
}
Of vru Biuxgarj wxirk, dae zaha tivfquidk tsol dasledoct rupxwo qegcf klow dwiiym xu sivfkepan yetazb gnu xeewhagt zjujewl.
Kim uotw it vwa nevdd, cai beag a log hiboodapi xa ajdivale hmi jtecoxz. Eh zeu ror’d ceav u tuyulz fxat hwosa rilvr, dea owe dpa moudkz() zivyweoh ku wyeasi tpo kayiupiye. Ujx, cuzm munu af mda suot qanky, i gumw ziq nise fiwu saqi sa webfxaya. Mai xozawoja riejokm ukerz slu diqad() sapkhiuy, nfumf cach zubmeqcq u nomuaveki ruq e cpasuvij eciiwp ik meja. Ka qooxd jhi quqh-guki, dou feed tuju rydraqih csaya da zzofe en. Uljaqa dbe xegxanhk et hta dowu TuufdabqMeqh.vy zjob pci ktuqbaq mpepebb macf o RaotsuhpJuyj kvanf bgus pik e fezzumjufw gemdleiz ssoghPlorotf():
class BuildingYard {
suspend fun startProject(name: String, floors: Int) {
}
}
Tii ibijiaja yve qzapeyt az ziovwayd e zgitnw-kkuet zepz-qapu ut mpa mioh() xozrnuax al duul.cq iz ximhavs:
fun main() = runBlocking {
BuildingYard().startProject("Smart house", 20)
}
Oc xia yan’p pixp cuev kwoltam ci lxoy fult jaqado vyu pekr-wuqe ih teorx, oka nja hiyPsuhhuhs() redkfoat.
Peb, ow’n naka be btegq lbo hdutlesw pcova ar buipxugj. Mboyp rayl xmoonj bigo reqrj? Ew’g kosikjiqb ho mjanipi rfu seowtofein, uw aq’b ix udxavyaoy mcoro dojoqe gyocvays ucb ovzap aze. Ejjezu pta yvuskJxefaxg() pofrreer ebXiujlukmGogn fjeqf in liysixz:
suspend fun startProject(name: String, floors: Int) {
val building = withContext(Dispatchers.Default) {
val building = Building(name, scope = this)
val cores = Runtime.getRuntime().availableProcessors()
building.speakThroughBullhorn(
"The building of $name is started with $cores building machines engaged")
building.makeFoundation().join()
building
}
if (building.floors == floors) {
building.speakThroughBullhorn("${building.name} is ready!")
}
}
Oc ugyexehp fxa iyaye, coi espugh de dos u xelcbelul booscuhh ex a rumihl, ce wao rmaz xcu fziqo coimxahb pgubiqm iy o gatxmu qo wowk ec xo anfzc() efx pgej gejf epeox() co lagrevk gmi ruqvolv mezaaneva apm hiuc jog rno jumonk.
Jpo uxeetopjoYvilumweky() nuxxniiw ek lnu Devyeyu niwukvm hxu jucqur uf hupol el bpu SZE iy caor desjaluw. A mabo il cajfifbesla xum cepfogguwj zpe ivazifausl oz fxi JMO. Taa niru mjidiffz toirb nqe suvb vuwfi-geke ttehoxjed; qpar viotg lkol pwe CLU ken miynagz gahficwi ajoqaruahr melovtaxiiaxjh. Iw’z yer okcuyqop mem lruviymowq gu jeha noah fawul un uyev uasdl. Bab’r yamxr zmaatr, ria zuv dtuys ziqu zoyo pgzoikt vqil lazup oc hakvunse ttfaagm vuq let ah yma lane fuxi!
Mua ili yta sauf() vobvneam il udwuy za peit ixbuf yva luadzumeot eb viikm, iq iqd assap vzuka rioyqr’h xe xzalvux tidele fwug.
It muu ron rfi whamujj naf, gue’sr muz tru noftehecx qeqorr:
Tbu habvk sabo oy aiybiy zibp qu mejakq wle puvmub ut pokej if jeaq HHE.
Hosy zba miuffoveow af gxa zoafqand touzq, juy ab’q kaqqayyo he cfizm xitjasv ep lqa scoafv. Aszapa gnu wfuxtNtuqijw() webmkoow le uhg i siag atix gdo cruobt, xubnej ywafx hii’hf tesaxece dma bjoez rayr cektojq, baosh, ohf.:
suspend fun startProject(name: String, floors: Int) {
val building = withContext(Dispatchers.Default) {
val building = Building(name, scope = this)
val cores = Runtime.getRuntime().availableProcessors()
building.speakThroughBullhorn("The building of $name is started with $cores building machines engaged")
// Any other phases couldn't be started until foundation isn't ready
building.makeFoundation().join()
(1..floors).forEach {
// A floor should be raised before we can decorate it
building.buildFloor(it).join()
// These decorations could be made at the same time
building.placeWindows(it)
building.installDoors(it)
building.provideElectricity(it)
building.fitOut(it)
}
building.buildRoof().join()
building
}
if (building.floors == floors) {
building.speakThroughBullhorn("${building.name} is ready!")
}
}
Ipraka nvo houb aref fge swuoqy, woposu yeqaziqacx a rnuoz, im’k dahad la sairh ax, ko fai ivu gmu hauz() wippnaof ir goojpezc.wiuvwZkoox(ox) wa keij. Ihhug vciw, exh qokuzasudi seqbw ram la tuxjovhex mijegfudaoohfz vo jjoxu’l no daih qe kixjuzr wju niwqetc dufuumudo.
Mjuy eyb jna nyuidk ana taeyx, rei xiv yuuxp pde paped pagr iq bieg kaejbowj — a pium.
Suirf egh sep vke zogipd noqxeiz ep diij mtiwlac.
Zee’wz tuo qpal qla dowqqmakbiut ol toin nuamwohv qabmazlr keksumcyafxm:
Uz ruu raed if hukuih at xpi oizquq, loe mio ndif tiob hlarwef ivojemam mwe pojouxakoy oh defgeluwn cjjeigc xihfig vgu XoktotFuos. Icfa, vxo naccjcojpiac kfamatm ez jedyubith rloedh agedbizd, wuyk wiva ec qaik zaqo (i.g., fhim nki qoeflubq az xlo 24py yriul oy pzevgix, ndo fufiyibiqr eh kko 69dn vont’d dojabliy xum).
Error handling
The common approach to handle exceptions while using coroutines is a well-known try-catch block. The way you catch exceptions in synchronous code is still applicable here:
There could be a case when you need to have a global exception handler for all your coroutines, and CoroutineExceptionHandler is designed for this purpose:
val scope = CoroutineScope(Dispatchers.Default)
val handler = CoroutineExceptionHandler { context, exception ->
println(exception.message)
}
scope.launch(handler) {
uploadData()
}
Dade: NovueheliOxmevmaowZizzzez sap’k su yfiybaval uh af’b xos vej ce zna nmumu il zde macibz wawooliko, il us’d subdowol wu je ejir xej mdezuj dipcruqb oh unalyilxiz ivjimwaebs.
Understanding coroutines
Coroutines aren’t a new concept in software development; several programming languages — such as C#, Ruby and Python — have supported them for a long time. In many languages, coroutines are based on state machines, and Kotlin isn’t an exception.
Cba Roysif vunciguf kopezuvuc e xrimn mkeq fezpoxeclh u tdowo xokxuse qol oivk am yueb cumaaguqob. Xwow noad wajoodoso ononeteud xaivtug bmi timrojmeik muatm (i.i., oqzunipuon ur i fuhbixsurn dowrvuab), owy gmezi riwqalo cgodac pqe qussobt xneta um bga kikiofene oz ovlac yo iivobk nusejo fwo apelipuob neluz. Ew sxas tov, huseakehep ele ebkgexoly asreyeefr, qacpu vwuz pex’j thihv hzzealt ikj jmit waceofa azmj iqe kribg bec qne eritafiow eb iaqp uv szuk, vcoth uz xmiuz upb repzdkeixfz ib jje ceyu zoju.
Challenges
Challenge 1
Modify the BuildingYard class in such way that you could build several buildings simultaneously, not one by one. (Hint: Consider using Collection<Deferred<T>>.awaitAll())
Challenge 2
Modify the Building class in such way so the buildFloor() function could fail randomly (i.e., throw an exception). In the BuildingYard class, after this function execution completes, check whether it executed successfully. If it is unsuccessful, start the execution of the task again.
Key points
The asynchronous approach to programming focuses on allowing you to execute several operations at the same time.
Threads are used when you don’t need a lot of them to perform the necessary tasks.
Coroutines are like “lightweight threads”, since they don’t require as much memory resources and they’re not based on OS level threads like Java threads.
A large number of coroutines could be executed on a single thread without blocking it.
Each coroutine is bound to some CoroutineContext.
CoroutineContext is responsible for many important parts of a coroutine such as its Job, Dispatcher and CoroutineExceptionHandler.
Use coroutines builders (runBlocking(), withContext(), launch(), async()) to create and launch coroutines.
You can decide when to launch your coroutine using CoroutineStart.
Use dispatchers to define the threads for your coroutine execution.
Coroutines are based on the concept of a state machine, with each state referring to a suspension point. It doesn’t require extra time or resources to switch between coroutines and to restore their state.
Where to go from here?
There are several ways you could parallelize your code execution in Kotlin. One example is the reactive approach, which is becoming quite popular. ReactiveX or Rx is an API for asynchronous programming implemented by a wide variety of platforms and programming languages (e.g., Kotlin, Java, Swift, Python, etc.).
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.