Coroutines Overview – Kotlin Programming Language

Coroutines Overview – Kotlin Programming Language

tudip-logo

Tudip

13 May 2020

Coroutines are light-weight threads. In a sense of some CoroutineScope, they are released with the start of a coroutine constructor. Here we are launching a new coroutine in the GlobalScope, implying that the lifespan of the new coroutine is constrained only by the current application’s lifetime.

import kotlin.coroutines.*

fun main() {

    GlobalScope.launch { // launches a new coroutine in the background 

        delay(2000L) // non-blocking delay for 2 seconds 

        println("Back") // print after delay

    }

    println("Welcome") // main thread continues while coroutine is delayed

    Thread.sleep(3000L) // block main thread for 3 seconds 

}

The scope builder specifies the scope of the coroutine. Some coroutines scope builders include runBlocking and coroutineScope may seem to be comparable in light of the fact that the two of them hang tight for its body and every one of its childs to finish. The fundamental contrast between these two is that the runBlocking technique obstructs the present thread for pausing, while coroutineScope just suspends, discharging the basic thread for different uses. As a result of that distinction, runBlocking is a normal method and coroutineScope is a suspending method.

Cancellation and Timeouts

It is also possible to cancel the coroutine while running and add timeouts to them. The main reason behind these concepts is there is no need to execute the coroutines when page loading is not required or execution takes more than the expected time. The cancellation throws the cancellation exception. The timeout is added by using the withTimeout().

val jobToCancel = launch {

    try {

        repeat(100) { i ->

            println("jobToCancel: I'm running in a loop $i ...")

            delay(300L)

        }

    } finally {

        println("jobToCancel: running in finally block")

    }

}

delay(1000L) 

println("main: Done with waiting")

job.cancelAndJoin() // cancels the current job and waits for its completion

println("main: Can stop")

Composing Suspending Functions

The suspending functions are created by suspend keywords. These functions are called from other suspending functions or coroutines. The suspending functions are sequential by default. Consider the following example:

suspend fun doInBackgroundOne(): Int {

    delay(1000L) // doing some operation

    return 10

}

 

suspend fun doInBackgroundTwo(): Int {

    delay(1000L) // doing some operation

    return 30

}

 

val completionTime = measureTimeInMillis {

    val firstTask = doInBackgroundOne()

    val secondTask = doInBackgroundTwo()

    println("The final result is ${firstTask + secondTask}") // The final result is 40

}

println("Finished in $completionTime ms")  //Finished in 2017 ms

Here, the execution is sequential. After executing the first method, the second is executed then the result is printed. It is also possible to execute them concurrently using the async block. And the async can be made lazy by setting the start parameter to CoroutineStart.LAZY then by calling start() method.

val taskOne = async(start = CoroutineStart.LAZY) { doInBackgroundOne() }

 // some operation

 taskOne.start() // start the taskOne

Coroutine Dispatchers

Coroutines consistently execute in some setting scope to an estimation of the CoroutineContext type, characterized in the Kotlin standard library. The coroutine setting has a lot of different components. The main components are the Job of the coroutine and it’s dispatcher.

launch {  // parent context, main runBlocking coroutine

    println("main runBlocking      : Current thread is  ${Thread.currentThread().name}")

}

launch(Dispatchers.Unconfined) {  // also works in the main thread

    println("Unconfined            : Current thread is ${Thread.currentThread().name}")

}

launch(Dispatchers.Default) {  // works in the DefaultDispatcher 

    println("Default               :Current thread is  ${Thread.currentThread().name}")

}

launch(newSingleThreadContext("own New Thread")) {  // runs in own new thread

    println("Own New Thread: Current thread is ${Thread.currentThread().name}")

}

At the point when launch{ … } is utilized without parameters, it acquires the specific situation from the CoroutineScope it is being propelled from. Right now, it acquires the setting of the fundamental runBlocking coroutine which runs in the main thread.

Dispatchers.Unconfined is an exceptional dispatcher that likewise seems to run in the main thread, yet it is, truth be told, an alternate system that is clarified later.

The default dispatcher is a separate one, to use this type then the coroutines are given with GlobalScope and  given by Dispatchers.Default and utilizations a mutual pool of threads, so launch(Dispatchers.Default) { … } and it is same as GlobalScope.launch { … }.

newSingleThreadContext makes a thread for the coroutine to run. A committed thread is an over the top expensive asset. In an application it must be either discharged, when never again required, utilizing the nearby capacity, or put away in a top-level variable and reused all through the application.

search
Blog Categories
Request a quote