paint-brush
Fundamental Coroutine Aspects in Kotlinby@azamatnurkhojayev
1,043 reads
1,043 reads

Fundamental Coroutine Aspects in Kotlin

by Azamat NurkhojayevAugust 8th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Coroutines are lightweight threads providing better use of apps they are operating on. Kotlin coroutines share threads, let’s say, from a respective pool. They execute a prolonged operation and wait for it to complete without blocking. As you can’t manually control the threads performing them, you can use them to get the most out of your time while waiting for the build to complete.

Company Mentioned

Mention Thumbnail
featured image - Fundamental Coroutine Aspects in Kotlin
Azamat Nurkhojayev HackerNoon profile picture


What phenomenon are coroutines? These are basically lightweight threads providing better use of the apps they are operating on. Furthermore, there’s no need to manually control the threads performing them.


The developer stays away from the lifespan of threads as new abstractions added by coroutines ensure context switching between tasks. Thus, the operational time of the threads is used more rationally.


It doesn’t take long to finally get a result from a notoriously sluggish input/output task. On the contrary, it keeps on processing tasks issued by other coroutines.


A developer’s typical work day involves code building and each one takes around 3-5 minutes. Meanwhile, they check emails and Twitter, do some stretching, or simply chillax, you name it. This is where coroutines step in. You get the most out of your time while waiting for the build to complete.


Moving forward, the idea of Kotlin coroutines is not brand new. A lot of programming languages make use of them, such as Go (goroutines), Erlang, and Java (Project Loom).


Installation Stage

Only a few keywords are integrated with the Kotlin library. This is not a big deal. You simply add a dependency on kotlinx-coroutines-core in order to enter coroutines. That wouldn’t take much effort. Check it for yourself.


Gradle:


dependencies {
   implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}


Maven:


<dependencies>
 <dependency>
     <groupId>org.jetbrains.kotlinx</groupId>
     <artifactId>kotlinx-coroutines-core</artifactId>
     <version>1.6.4</version>
 </dependency>
</dependencies>


As long as the library gets updated, variations alter.


An Easy Coroutine

Let’s consider a limited-scope example below to get familiar with coroutines. Keep in mind that most of the upsides aren’t shown by this pattern. The more you learn about coroutines, the better you realize their benefits.


First, check this out:


fun main() = runBlocking {
   launch {
     delay(2000)
     println("and it worked for me!")
   }
   print("My first coroutine, ")
}


Here a code activates a coroutine with a delay before proceeding to the next step - printing out. The laconic example contains a couple of functions to hold your attention. Take a close look at them.

  • runBlocking - runs a new coroutine that blocks the ongoing thread until its completion. In other words, the thread that runs it gets blocked for the duration of the call, until all the coroutines inside complete their execution. The pattern here only illustrates the example.
  • launch - is a so-called coroutine builder. It launches a new coroutine without blocking the current thread. Additionally, launch inherits the CoroutineContext from the present CoroutineScope.
  • delay - suspends the coroutine for a time. It temporarily halts coroutines from lasting as Thread.sleep does. But unlike that method, the underlying thread does not get blocked. Alternatively, the coroutine suspends. It releases its thread enabling another coroutine execution. A thread is obtainable when delay is over, and the coroutine continues where it stopped.


Clarification of the terms

A few words are to be explained.

  • Suspending a coroutine. Coroutines share threads, let’s say, from a respective pool. It is a function that can be paused and resumed later on. They execute a prolonged operation and wait for it to complete without blocking. That is the exact advantage of coroutines: actually running coroutines strikingly excel the available threads in number.
  • CoroutineContext - an interface that marks an element/set of elements. It determines the context in which the coroutine runs.
  • CoroutineScope - designates the scope for new coroutines that is utilized to track their lifecycle. It is composed of CoroutineContext of the recent coroutine activated inside it. Specifically, extension functions of CoroutineScope produce new coroutines. As you can see from the example, runBlocking is rendering its CoroutineScope to the further call to launch.


Suspending Function

What kind of function is that?


Such a method has already been shown in the example above – delay. The compositions of delay are:


public suspend fun delay(timeMillis: Long) {
  if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
      cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
 }
}


Luckily, Kotlin language contains the suspend keyword. What is the point of it?


You are aware that the functions that suspend through their execution at a point are marked as suspend. Honestly, it takes a thorough getting into details to provide a comprehensive explanation on the matter. Otherwise, figuring out when functions suspend may be really tangled.


When do you annotate the function with suspend? Just go by the book, if a function is prefaced with suspend, and the calling point is outside the coroutine.


Compare a calling point outside a coroutine:


suspend fun printAfterDelay() {
  delay(2000)
  println("and it work!")
}


To a calling point inside a coroutine on the other hand:


fun CoroutineScope.printAfterDelay() = launch {
  delay(2000)
  println("and it work!")
}


By the way, have you noticed that both examples are insignificant code refactoring of the original one?


The compiler will come in handy in various situations, this is the exact benefit of a keyword. Thus, compiling the below:


fun printAfterDelay() {
  delay(2000)
  println("and it work!")
}


Will eventually result in such an error:


Suspend function 'delay' should be called only from a coroutine or 
another suspend function


Note that this is an instructive explanation before you technically advance into action and become an expert in coding.


Summary

Kotlin coroutines are considered lightweight threads, a means of providing program multithreading in the sense that they can be implemented without the use of context switching mechanisms by the operating system.


They split and output their underlying resources when a certain coroutine hits a suspension point. This enables efficient usage of application resources since threads don’t have to be blocked during long-term tasks.


Wrapping up, we had a look at a simple pattern of an easy coroutine and a suspending function. Lastly, we touched on the suspend keyword and how to operate it.


The given examples don’t illustrate the real value of using coroutines. The article describes only the basics. Keep an eye out for further in-depth guidance on the subject.