We're planting a tree for every job application! Click here to learn more

Coroutines in Kotlin

Amit Nadiger

28 Oct 2022

•

6 min read

Coroutines in Kotlin
  • Kotlin

Co-Routines means cooperative routines . i.e routines which cooperate with each other.

It means when one routine is executing , the other routine will not interfere with 1st coroutine w.r.t to memory , cpu ,any resource by suspending itself i.e without blocking.

Coroutines don't have a dedicated stack. It means coroutine suspend execution by returning to the caller and the data that is required to resume execution is stored separately from the stack. Coroutines share the stack due to support for suspension.

How to convert the function into coroutines. Its very easy in Kotlin - prepend the suspend keyword to the regular function as below :

suspend fun backgroundTask(param: Int): Int {
// long running operation
} 
Under-hood conversion of Suspend by the compiler: 
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
  // long running operation
}

a new additional parameter of type Continuation is added as above .

Continuation is an interface that contains two functions that are invoked to resume the coroutine with a return value or with an exception if an error had occurred while the function was suspended.

ContinuationPNG.PNG

How to create coroutines in Kotlin :

CoroutineBuilders help in creating coroutines. Since CoroutineBuilders are not suspending themselves, they can be called from non-suspending code or any other piece of code. They act as a bridge between the suspending and non-suspending world.

Kotlin coroutine builders

runBlocking: blocks the current thread until all tasks of the coroutine it creates, complete.

Typically, runBlocking used to run tests on suspending functions. During tests, to make sure not to finish the test while we are doing heavy work in test suspend functions.

launch: “fire and forget” coroutine builder no return to caller.

1.launch creates a new coroutine that won’t return any result to the caller.

2.It also allows to start a coroutine in the background.

fun main()  {
    GlobalScope.launch {
        println(doSomethingHeavy())
        ---- do something ----- 
    }
    runBlocking {
        delay(3000L) // Keep JVM alive until coroutine is completed.
   }
}
suspend fun doSomethingHeavy() : String   {
  delay(2000L) // simulate long running heavy task
  return "Did some heavy operation that was 2 seconds long"
}

O/P : After 2 sec it prints the string: "Did some heavy operation that was 2 seconds long".

async: coroutine builder which returns some value to the caller.

Can be used to perform an asynchronous task which returns a value and achieves parallel execution .

This value in Kotlin terms is a Deferred value which is the equivalent of a JavaScript promise.

We can call await on the deferred value in order to wait and get the result.

Use async for results from multiple tasks that run in parallel.

val userId = 1 // UserId 
fun main() {
  println(“get userName from Sever ")
  GlobalScope.launch {
     val userName= async {
         getUserNameFromServer(userId)
     }
     val  userAge = async {
         getUserNameFromServer(userId)
     }
     if (userName.await() && userAge.await()) {
         println(“UserName of userId($ userId is: ${ userName .await()} with Age = ${userAge.await()} ")
     } else {
         println("Please wait ,till both userName and userAge are fetched!") }
     } 
  }
  println("coroutine is waiting for a result...")
  runBlocking {
      delay(3000L) // only used to keep the JVM alive
  }
}

suspend fun  getUserNameFromServer  (Int:userId): String ?{ // This is coroutine 
   var UserName:String? = null
   UserName  = // Do network call to get user Name based on userId
   return UserName
}

suspend fun  getUserAge (Int:userId): Int? { // This is coroutine 
    var UserAge:Int? = null
    UserName  = // Do network call to get userAge based on userId
    return UserAge
}

O/P : Here async builder will suspend the coroutine (getUserNameFromServer () and getUserAge(). async will return a Deferred value as the result of getUserNameFromServer () by calling the suspending function await() on it. await() will fetch us the result that getUserNameFromServer ()/ async returns.

While getUserNameFromServer () is executing, the following happens: async builder will suspend the coroutine (get sername from the web server ).

The execution of other tasks i.e getUserAge() also continues.

Once getUserNameFromServer () returns a userName, userName is stored in the global variable .

Step 1 to 3 will be executed for getUserAge() also.

Once both userName and UserAge is fetched , it will be printed.

withContext: another way of writing the async without writing await().

Use withContext when you do not need the parallel execution.

Both withContext and async used to get the result which is not possible with the launch.

Use withContext to return the result of a single task

Below example (Copied from the android developer site ):

suspend fun fetchDocs() {                                  // Dispatchers.Main
   val result = get("developer.android.com")   // Dispatchers.Main
   show(result)                                                       // Dispatchers.Main
}

suspend fun get(url: String) =                  // Dispatchers.Main
    withContext(Dispatchers.IO) {              // Dispatchers.IO (main-safety block)
        /* perform network IO here */           // Dispatchers.IO (main-safety block)
    }                                               // Dispatchers.Main
}

runBlocking and coroutineScope may look similar because they both wait for their body and all its children to complete. The main difference is that the runBlocking method blocks the current thread for waiting, while coroutineScope just suspends, releasing the underlying thread for other usages. Because of that difference, runBlocking is a regular function and coroutineScope is a suspending function.

CoroutineScope:

-- Where (on which thread) to run coroutine.Defines a scope for new coroutines. CoroutineScope is nothing but a CoroutineContext => But intended use of both are different .

CoroutineScope keeps track of any coroutine it creates using launch or async

In Kotlin, all coroutines run inside a CoroutineScope.

Whenever a new coroutine scope is created, a new job gets created and & associated with it.

A scope controls the lifetime of coroutines through its job.

Every coroutine created using this scope becomes the child of this job.(this is parent & child relation in coroutine)

If any of the coroutines throws an unhandled exception, it’s parent job gets canceled. Ex: scope.cancel() . When a parent job is cancelled ultimately cancels all its children. This is called structured concurrency

public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

Structured concurrency do below three things:

  • Cancel work when it is no longer needed.
  • Keep track of work while it’s running.
  • Signal errors when a coroutine fails.

CoroutineScope is an interface that has a single abstract property called coroutineContext. Every coroutine builder (like launch, async, etc.) is an extension on CoroutineScope and inherits its coroutineContext to automatically propagate all its elements and cancellation.

Ex: On Android, you can use a scope to cancel all running coroutines when, for example, the user navigates away from an Activity or Fragment.

Coroutine Context:

Coroutines always execute in some context that is a set of various elements. Below are main elements of coroutine context

Job – models a cancellable workflow with multiple states and a life-cycle that culminates in its completion. Launch returns a Job object.

When you launch a coroutine, you basically ask the system to execute the code you pass in, using a lambda expression. That code is not executed immediately, but it is, instead, inserted into a queue

A Job is basically a handle to the coroutine in the queue. It only has a few fields and functions, but it provides a lot of extensibility. For instance, it’s possible to introduce a dependency relation between different Job instances using join(). If Job A invokes join() on Job B, it means that the former won’t be executed until the latter has come to completion. It is also possible to set up a parent-child relation between Job instances using specific coroutine builders. A Job cannot complete if all its children haven’t completed. A Job must complete in order for its parent to complete.

Job is essentially a task that runs in background, and it can be interpreted as an action, with a lifecycle that gets destroyed once finished.

You can even establish a hierarchy, with parents and child jobs, so that when you cancel the father, the children also get cancelled.

We can run different operations using jobs:

Job.join blocks the coroutine associated to that job until all the children jobs finish.

scope.launch {
val job = CoroutineScope(Dispatchers.Main).launch {
     val foo1 = suspendFoo1()
     val foo2 = suspendFoo2()
     doSomething(foo1, foo2)
}
job.join()
    callOnlyWhenJobAboveIsDone()
}

Here callOnlyWhenJobAboveIsDone() is called only when doSomething() is finished i.e means even suspendFoo1 and suspendFoo2 are finished.#### Job.cancel: Cancels all the children jobs.

Dispatcher :

Determines what thread or threads the corresponding coroutine uses for its execution. With the dispatcher, we can confine coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unconfined. When I say coroutines can run on more than one thread , how to decide which thread they can run on:

CoroutinesDispatchers comes to rescue:

CoroutinesDispatchers

Dispatchers specify where the coroutines should run i.e Dispatches work to the appropriate thread.#### Dispatchers .Main: suitable for main thread on Android UI and perform light work .#### Dispatchers .IO: Suitable and optimized for Disk and Network IO operations , data base operation ,etc##### Dispatchers .Default: Suitable or optimized for CPU intensive work such as sorting operation etc .

Below is copied from the Kotlin site :

import kotlinx.coroutines.*
fun main() = runBlocking<Unit> { 
    launch { // context of the parent, main runBlocking coroutine 
        println("main runBlocking : I'm working in thread ${Thread.currentThread().name}") 
    } 
    launch(Dispatchers.Unconfined) { // not confined -- will work with main thread 
         println("Unconfined : I'm working in thread ${Thread.currentThread().name}") 
    } 
    launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher
        println("Default               : I'm working in thread ${Thread.currentThread().name}")
    }
    launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
        println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
    }
}

O/P :

Unconfined : I'm working in thread main

Default : I'm working in thread DefaultDispatcher-worker-1

newSingleThreadContext: I'm working in thread MyOwnThread

main runBlocking : I'm working in thread main

Further reading : https://github.com/Kotlin/KEEP I hope you will find this article useful in some way !

If you have any questions or suggestions , please leave get in touch!


Did you like this article?

Amit Nadiger

See other articles by Amit

Related jobs

See all

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Related articles

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

WorksHub

CareersCompaniesSitemapFunctional WorksBlockchain WorksJavaScript WorksAI WorksGolang WorksJava WorksPython WorksRemote Works
hello@works-hub.com

Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ

108 E 16th Street, New York, NY 10003

Subscribe to our newsletter

Join over 111,000 others and get access to exclusive content, job opportunities and more!

© 2024 WorksHub

Privacy PolicyDeveloped by WorksHub