We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

Login or register
to publish this job!

Login or register
to save this job!

Login or register
to save interesting jobs!

Login or register
to get access to all your job applications!

Login or register to start contributing with an article!

Login or register
to see more jobs from this company!

Login or register
to boost this post!

Show some love to the author of this blog by giving their post some rocket fuel 🚀.

Login or register to search for your ideal job!

Login or register to start working on this issue!

Login or register
to save articles!

Login to see the application

Engineers who find a new job through Functional Works average a 15% increase in salary 🚀

You will be redirected back to this page right after signin

Blog hero image

Coroutines in Kotlin

Amit Nadiger 11 May, 2022 | 6 min read

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 in to 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 { // 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 {

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 a comment.

Related Issues

open-editions / corpus-joyce-ulysses-tei
open-editions / corpus-joyce-ulysses-tei
  • Started
  • 0
  • 16
  • Intermediate
  • HTML
open-editions / corpus-joyce-ulysses-tei
open-editions / corpus-joyce-ulysses-tei
  • Started
  • 0
  • 5
  • Intermediate
  • HTML
open-editions / corpus-joyce-ulysses-tei
open-editions / corpus-joyce-ulysses-tei
  • Started
  • 0
  • 5
  • Intermediate
  • HTML
open-editions / corpus-joyce-ulysses-tei
open-editions / corpus-joyce-ulysses-tei
  • Started
  • 0
  • 7
  • Intermediate
  • HTML

Get hired!

Sign up now and apply for roles at companies that interest you.

Engineers who find a new job through Functional Works average a 15% increase in salary.

Start with GitHubStart with Stack OverflowStart with Email