First Steps With Kotlin Coroutines

Written by wirtzleg | Published 2022/09/07
Tech Story Tags: kotlin | coroutines | java | asynchronous | reactive-programming | kotlin-co-routines | programming | coding

TLDRAsynchronous programming is gaining momentum because it allows much more efficient use of processor time. There are several approaches and coroutines are one of them. There are 2 scenarios described here - how to call suspend functions from regular functions, and how to call blocking operations from suspend functions.via the TL;DR App

Asynchronous programming is gaining momentum because it allows much more efficient use of processor time. Thus, the application can handle more requests per unit of time and is better scaled.
Today there are several options for implementing asynchronous approaches:
  1. Callbacks
  2. Reactive programming
  3. Coroutines

Coroutines

Coroutines allow you to keep the application code in its usual form, performing all the operations of switching between tasks under the hood.
suspend fun doSomething(arg: Argument) {
    val firstValue = sendRequest(arg)
    val secondValue = sendAnotherRequest(firstValue)
}
In the example, we mark the doSomething function with the suspend keyword, which means that the answer to it will not come immediately. The function itself also executes some asynchronous requests one after another.

Calling suspend functions from regular functions

The suspend function cannot be called from a regular function, because it is unclear for the JVM on which thread it should be executed.
If we want to execute a function on the same thread, we can use runBlocking. Of course, this option will not allow us to efficiently utilize CPU resources. But it can be used in tests or command line interfaces.
fun casualFunction() {
    runBlocking { suspendFunction() }
}

fun suspend suspendFunction() {
    doSomething()
}
Another way is to use a separate thread pool. The example below uses Dispatchers.IO, which defaults to max(64, number of cores), but you can set a custom thread pool.
fun casualFunction() {
    CoroutineScope(Dispatchers.IO).launch {
        delay(1000)
        println("Launch finished")
    }
    println("Casual function finished")
}
It is important to understand that the call to the launch block will occur in a different thread, and the method will continue its execution, so “Casual function finished” will be printed out first.

Run blocking operations inside suspend functions

What if we need to run a heavy function (e.g. IO operation) inside suspend function? If we just run it, it will block the current thread, and it can ruin the whole idea of async programming.
Instead, we can define a separate context for slow operations so as not to block the existing threads.
suspend fun suspendFunction() {
    val context = Executors.newFixedThreadPool(10).asCoroutineDispatcher()

    withContext(context) {
        runSomethingSlow()
    }
}
Now, an outer dispatcher will suspend and delegate the control to our custom context. When it is finished, the custom context will return the control to the outer dispatcher.
That is pretty much it to start using coroutines. You can find the complete documentation here.

Written by wirtzleg | Senior Software Engineer at Farel
Published by HackerNoon on 2022/09/07