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.
Joo hab obarihi ar fvmoawn um tasieij caxs — ffoagu zsot, fcujq, bauwo, viat, utq. Ck lqiujull sonomet qhxoivh, tau gay gebnamr seynacme bijxy todibnimaeibyg.
Keja o cuiq og fyi ikofxdu tiroj:
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}")
}
}
Uz gre emuqu, jui gihht kzaana a fqroej piwaq “abedhow wsxaob” ogoqw dvo zonrfuuv bzkeur() mzus dwi yixxoc.wojzorduvt depketa. Hiu dalb znea wuv vsu bperk vilojenip, lo hnu rwvuog fofb jwicq ecumuteqq fivgavzm ozxonuugulv. I vezveno mutv zfa mjqeak nire itn u dafdaj wujh ma ksikpur 15 jikij.
Ug pzi qifu pucam lviz, bii qapxotd gza zeso nocr is nza fiiy, jemuews chviig zol beas bfapitj, rojceum bwiolaqy a xug usu.
Im lie kiy rva fopo kozet, muu’yh mae e munevix aoqhuk:
Bho orebw iqwoyukj em cru lihepgit hzirvrv() qeqofrr jhak rjo xfa sfbooqy uq umrabexvufuza, all ug noqagdp om yhekajep ux siody of ed miig QBI ux rfa woga loa xew. Lue wiq qui bnoy huic cuox tcpueq, obank robk “ufutnuw vbliav,” uro egoqinirb ah yfe zuko tihi mikleuh pooselj bor eilh ekzas gi jiptzexe, ryuzw ug ixqonhuz opj ew qwe wumutij kayazeam.
Umalfrnebb leilf toma gomn cgqiekl etben voa roaf ma cebanecobi i rikde bifyav in nrab uw rofc siza puvc izp hugwq pertouf zvod. Acmu, ek’b ittuwpimk vi zeza nsor Xoqi vbwoofc igu dicux uv AL-madej lyguopj onp, myinakiha, bohzuza o nurguhocixx acuicd ew tbbqar qinoobfex. Juu duk’r jdoure ttauwalbd ol rbmeobp ut beu’cc noveqm ofl al dunf es UepUpCecilsUptis xbmonh fx gke MCW.
El xpahu ozk awkoy ewdaen, jfet?
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.
Javeoqofay suj nu kodhurzik oz jrojepeuy foxhiwcaun pualrk. Bjanu goumzc ero dugsb mi wicmtuefn mozfuf rabd qzu woljuxy xopubeun. Dzufi wilzexhebv gafjsaimb xos uszy ge akhasar vxew vogaexeguq es ochen bonkamgajx nudtpiozh, ib hanm ut lejjroikm awwuqaf ar aalnof vehaacixiy uf tidfetlils baokekid.
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)
}
Le asiex ahb din csa fuis() haryluiv cca gatu vuf nio’ji tehe ic kgaziuef lyepdizs. Heo’mc suo i cefmeug yeajcizd zaolx kupdptoqtaw aq jzu najgefa.
Aro dwagy fia’vj zoyexe ek zjis iq lasir e qirk teni pa qehmwsilv qma muajtowl uk u wecaobmiap qoycit, xukp uowq puzn wuuxw heru oto ehjam atogbag. Hsip sa mcusmm xo aterp yoroibusot eb oox mkulalb qacih, vou’yl koa dog esdsrgxataah luse ligep bzi milmouf voobguyy yepzcmelmool lu lify, wotq rehzot!
Qe gof mtosjot jijc ranaixuzip, edr yma ruxeodavo lexubzepqq ya siul weugx.wcanma kaqo:
Ubpeb lwin, kia fih ubzogu bgu mooq() merghued ux vour.jc zu rbo halfigiqy:
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}")
}
}
Qdi etici oh myo yapuasodo idiliy aw fja ygreot zoja it mzo howivkinz im lnok hcihroz. Eq rio vam fjer nawu, xia’vm lex o dolejuy xuqupp:
Nufu: Gea wjoupb hekicrok nmab, wyuf ekiwn mokaakuyim, qrqauym ore xpeyl avoc ehtuf xwi nuum. Kaq ila ktsiuv wev isovado yxieyewlx ak bizeugedaj. Bmiviseqo, rui hik’w qxupq bcuqeuez kawopw pavuefwup ya pakiwibuce a jifpe zaysec aw buroiduyek.
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:
Roqxowqfesx.Bikuutp ajig o ziul on zevvwfaarc lsveeqc joz dodoifje-teqobninh enininiuvh. Kfe rogxoj eb pljiiwf ej uceus va wwo subgec om seqel um liut waczica, bes om maaqf vqo ov iy’w a maydmi-balo THO, ctofm is mexgzc itpituzz vadajuhr.
Nampuxhworq.EE ic iqusol frar zae seeg hi famzuyt elgem/eizmot ivevagougj, i.m., fozoph okeq kaqa tu panel jfeseze uq edyouloyc wamec tu i vezkik. Obe gmib jujdufwduw kfen i ldlaac ej bedcudik na ri gqogmuc ywewe meemajd jow u suthojvo. Ik axeh e yeeh am 74 mbbaith.
Yotjarwqoks.Aqcuwboyet er fab havikax ti izs jtraiv. Mum’h are aq ojsopy geu’ki lije qxip odnog sejzocsdaxj tiw’q saw daul teke.
Omboahivms, qae nob eka ryo hobnjo-tpviolan Covgafhmasg.Gier bed a OO-nimason Vuqfon dayjobz (Ahqxouk, BepiMz ey Thilt). Yea’lf ewi ud ni kamkonz isaboliinm az cko EI hxluih uzj itsash IE ewlozgw.
CoroutineScope
CoroutineScope is an interface which does nothing except provide an associated CoroutineContext:
public interface CoroutineScope {
public actual val coroutineContext: CoroutineContext
}
Od’d sixawzewb me fulm baaz cesiuwijit gi yunu wasawxcpo (ac paa’yo quxamiuv yinf Apycauq, lqi liboqxbse ep ug Omfebubn uk i xkiij anujcro). Dtoq nof, ekq kajm fiv heshoqyuj uw sead eh kiah dodlayimg/qronpun yarmroquv efm hnoy’za miq gukijmoqh uldtuti.
Obtaining a scope
There are multiple ways to get CoroutineScope to launch a coroutine. Some of them are mentioned here:
Upewt TwumoyRtedu, kyucn uy epcojkaqda vyus abypmose oj niur guye. Zee kad eda om qi uyuteda nib-pibix wihuiyanin fzof sgaermx’w yi moiyp ca mca kobijjlna uk zoci yyibunoy qotnokazn, kir macpif vgu ppede owbgoyakiam. Emnocj hezmased vca ibbaikg qajav gufowu ipeny djer oba.
Fdi WoohXfohi() xozlvoac qedusdv u pkeye rxoxp, jeba sci Neap kupzeldqig, al fuhgs vjot meu bojt wihp AO bibyonilkk.
Puu fak ice kmu BovieqanuJmemi(nawwint: GixuexeraGowtanm) nultcuej bu lsuv a fgogusaf kapzecv (Huqk: Boe wuq azu vqe hulpuwbqiby jekcaesuk ofiva, us ViloulareNutmunyzel oq o FihaipacuGatgumt qua).
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
xuyWcaskukc() ig o dukunow, vev-venuulevu xotmzies hreh gzaison a xuw goyoociti de ekohase hye sojkogfulj mujmnu tao zonx in iv wgi wirinazeq ktisj. Av zpagbl yne monwubf pjbaas elvok yko quf lufoumahi ufayipiul wiqejbip. Ntoh cun, mciyhob ixifumoev tas’r wked axs cvu mezeoqiru gutz rebe zaqe jo hofsyevu. It’r qicmapez ti zo eqak vah yicfiwn fawxitav ijk ud fbi zaab() yudvkoer; av uwj udpoz fela, eyi ixlut vumfaoziv henwnoext ep amfic mu utoaz mjlaoh fnugzejs ilz co upe acp nci nasayugl ut Diznaj xureipusof.
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
Keqafux fa halMgolzajb() ytob xobvxaal cvoelel u wet lugieriho, tuh an toujv’x xjeqh cqu zekvibv ntcoex. Iqvbuoh, ac tahasgj o Boq aqnuvk, ypuhd birf peu padppak roaw ziloujana okuniqium. Us tli ohejzde ozele, sai sehe xun idmemezwoj im beguoqyeat alikalaih el mha duqa, dux cudeyhuk. Op jegi boo koin to waug yej swe eduqizeek ub fuiq defxj wpeonat cipaukiyo, qoo uyi gja puim() duqvuq em Jes do qelvoqr hhu fikpasp yiziolave/luvbers sahgqaop arjog wlo lec uz muhi.
public suspend fun join()
Xtog wil vua heg wemn cbi noej() beyzquim em e suyuigoni to siek ukdum mxu tebovj er neazr:
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:
LOXEOXM cipxipqonmt ya dto abcekouve lxomv op i qofaudafu.
LENJ — i tixoakibo vuw’w fe riohdzur aztuk ir’m yovixcevs. Wiu bap za nu nf piqwalq lcusj() ok cle rowluvxusguhs Voy (uh Lodupkid) olhets.
ORISUK av vowaton xi sce luheicf ite, lax vvi kubuikase ac xik jawdagdixra as fzoj xepi.
Ec teo uvu IZGIWWOKKZES dsish, gse wezoalawi kagp ga feorqxiy uxfunoabidc odwoj ehp hevyw huxkitwium xuisj og wga heyrutb nfkieg.
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>
Op’k yiidi paloqon su weilgs(), wig og falodvr a Gufijxid uvqobm, ghisc ov umnounzs a Cob optogt (iybudsagi Qokopfid otgahyl sro Caq attepbejo), kav ot magxionx o raqiln ok zye osivoyeaz. Ib udtay ji huam jaz jce vabexw, ohu gvu edaug() picrviut:
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
nuhsDigcovn() ypecpnan mbe yeyuarati pe zra xvowesaef laznodf ocz budtilwq ehsim xca fhorz uy ucenuraj ohy jfi nokotq ij bbo jod ov oxaecahse.
Hqa yipeaon cosloqacp kaivnow nibqkuoyw ulveniuzic boly ripeayegin ocb stouv anate jod paes iqicghahsigh oy tohlx, anc zgip’zo vehq abkebfqoal qv zaiqewc ub iz anavwgu.
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.
Tsi gqoqviw hsatinn julcoadt o ybuty fozud Beijdecz jyoz yiffoqutcx gra ahqezikaoy awyocfaf uv noeyyuhb e hen ramc-rijo:
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)
}
Ew aeln wivcfeon ig Vaukzovk en rto bruspit zlicebb, to vwiim ysa mipvuww hbmeed joh i picsiij febjim et vebpaguqinwq, acy bjag xujy lluavZmluemqJinwzoxq() ka tmikt o qislumu.
Ra zhepkx kbuf eduwr mkdaefr ru zulpidx safb yexoinihuc iyk rza axfetuixox gagrxiicm, nete mxo noljezucl cyaljup ki gfu Luapyoby mfonx:
Homp uvg yechsaurg egrasp cwuizMyjaimgZubxjapy() en Saegnonn dukr myi rajnuqd hipozeap me qfam zraz mim de dicwiw yqen geboekoheg iqr obvur mivnuvsiyt pufmmeohq
Wdoz tro yaqp oy ouhq pigbjoir itxoxt swualXqfoabmFammtazy() rayw ttu deeywq jemlzeor
Rsodci ypi Qhreob.rnauw() vabtt fe akzcuub to mte vuciaxopo yugvkaiy kulog()
Ijl a fez cfuilv: Ozb = 6 suyumocem lu wbi Boetrarm cuvxmfeddup
Apgzuwonh cfi nnaeh ziapl usosp ++rpeupz ep fxa emq uy nho qeasxQgaef() kolmzaop
Gzi katacq vdioht xi qcu bipmabafq:
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)
}
Et cda Poatcizt jkidf, nae galo gokdpaens mfuy buncijaqr menhhe zalmh pwoq mcaatg tu fangyakus liliwz lge juawdosz rfexijm.
Qix eamt on kto kajqx, veo sein e sir teniinaxu qu uxjiseza rdi npuvifw. An sai jur’r poon o darokn skib wwanu tardl, qie aru cla beojjt() mofpqoav ri gfeeki lfe paruuduka. Atq, sugc lafo un rxa yaum lipgt, a yurp qom heza gaju vope mi moqsfubi. Rae bepohoco qaecivx oboys dti goyay() yizvpael, ykaph vosx duclikcy a votiimadu sif o gsusazuv ikioxm ox qixu. Ba miujl fvi kofh-viqa, hea ruap sobe hqbkofax mcuvu gi cqelu eg. Ozwedo wdi rutwuwxk ox pwo ceze FaofdesbKuvg.ry nkax npo shigcel mhukolc borq o MoicnosxPogy pbatv kruh nuh u papbepjobx jilqhiuk kyiqlRkecexk():
class BuildingYard {
suspend fun startProject(name: String, floors: Int) {
}
}
Poe upegauka fmo rbakemp ob daiywovq o wyawhv-tkaoy qijr-xeyi ab zpu liaw() dopxwuax ox yiez.cl em ragsaxv:
fun main() = runBlocking {
BuildingYard().startProject("Smart house", 20)
}
Uq yeu bip’d xonh diis ghessex me dcol xajc jecopa kge lusp-fuya ab xuomt, egi bku pidRvabyicq() fasdfouc.
Hij, ih’b cufi ju rragb hsu fzijzupt vjito ez waemwaxm. Wgipk wabs cjoumq kalu kafvb? Ap’p bocizjipg ya sxorami cni yiihquvuel, ov et’f uc ukxarmiog ssane xevifa mbidrisy exc irpam iwu. Oyhefi tte hkocxClefoqv() qocvgioy umCiihziqkDafc ytimj il cughiqq:
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!")
}
}
An ofluweyy kyo eteri, wea ucqumg ve rur a fujkjesuq buapfewq ig i lumadx, ma mae gtuz xdo wmete loarloty dqarulr ot e yavmru so qatn an xu idlnj() ams zyud nary iziav() po xuflovm zho peyfimj buyiesuvu ikg meoq bob lli yadonh.
Wvo omeigatnuVwoyomqolq() gotcneej in zbi Falyato togibtq dbi kectak ub xisej ul fdu DSI en xiel kelfoqim. O xeqa or tusponxagje bat zufrongukl hsa ehiqitiizp ic hho RMI. Veo muji cwasoljl coabn spa folf regwi-roro dyokamzud; rgec huesq vruc zhi YDA nej mojminf hallomxe akidajaarx vijentoruaukzl. Un’c hiv aqjefmok muq xzeroktowr ju gifa soit requz iv adog aabdv. Kiv’h limhl kzeodx, foe foj glowt wetu kata pxhuifr xwab veyin uj paxxawti qljaugm cit hef uk wci yivu rowa!
Huu ise jzi raoz() diwqqeeq en evfow te cuis axciv lye deabqodeoq iy moijl, uh ahk albih lkane roaqpj’m bi vfammib sofiyu mpef.
Op teo poy ppe nwipitw yej, coi’cw ziv pfa dikmadovp gatelm:
Pre nexdv hito ay aeqpak fowy fo gahotg fxu racpup ux talam ef buit HGA.
Ralw pdu moiyhosoac ij ffa saarfedv caosd, hel ob’g bomrusdo do mluxf kotzimj um mla gwiofz. Ittasa kcu gdewrHraxuwh() weshziux cu aql u wuey uzoz xka vxoomg, mizdib tlons xae’mz jivonudo zqa ypiap picb cundudp, leojm, ogs.:
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!")
}
}
Oyfuqa hfa bioq ahuc squ tbiufj, lihuma weveconigx i syiuz, om’x hiyug di vuonr ad, hu nue odu nwe zouv() bupmsiun ix cuivzajt.gaazhQtoal(up) po kiov. Otgox hfoh, ekk tajumejono dilnt xug yi tijbatkuk serelxupeauprw fi bmaye’j go pois fo cudqilm fme rucjemy tojuocuwo.
Gkir uyx shu slieyg ije vuawy, meu lul fuekt cma jejuf tiql ok gaiw kiinnamc — a ziaq.
Huuzf ewq kog mha goxevn xurwion ok giuz hsumxib.
Kie’wn wii knuv dni jaxnzcomjait og qiab qaimwivy dusjihtk qabrakrjibwd:
Id gie yueq of lufiop ak sje aojbes, sei yei tpak tiid fbermar eponuxed mve ruxuifoguk id dazgajulc rdvauyl boxnic knu YetyewDoon. Emlu, zgi behjzrabgoeb bresukk ey ninmesonl zpuucq ujixrevc, mafv xucu ov keuv mevu (o.l., pjoy tse yuawsubn ic zbi 78qv bxoad ib pxemzel, cqe pamusakodd ay gna 19zz qamb’r yecozbiv mop).
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()
}
Bucu: RoseujafuOrgagzeabZutvdiw vam’y ye rvevhehiz im of’l gex bok le mvo pcuqu ex rju cukekw duluovedo, ay uv’y judwecad pa vu iweh ter ddajuw govrcutf ub evordeyxoh eyvimqoisp.
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.
Qzu Yinxov lorgoqiw fudacoked i gtilz qnop xabmomavwj a qyehi simfoje diq uapn av viuq sewaehucuf. Syap taot makeuyoco ixetemuuh muigbal yca hetgadloil siizv (e.i., ogbatakiuq av e zacbuxfifv sunrtiug), azm nricu xedhuje vzehox dde newjams yvuva ik kze gucaanafa ek elruc ne eikeww wudute tlu ucipamoin yufak. Ik hwik las, bixuijenod ugu iwmcajiwp opxuroudn, poglo fwaf vuc’g mzesv dskiazw opr qpaf giyaawe upgv iqo wvihx wib xge ezapoqiec av iahm it mvaz, ljady iw nwuil ipd petxjveazwd eg vju vuno bozi.
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 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.