Hoinzey

Javascript. Kotlin. Android. Java

Kotlin: Coroutines - builders

There is a lot to cover when it comes to coroutines. This post just looks at builders.


There are however a lot of talented people who have put a lot of time into documentation making this powerful feature as welcoming as possible. Try out Kotlin's own docs, the team at Baeldung or maybe MindOrks


Builders

The first step in creating a new coroutine is to use one of the available builders. The next step is picking the right builder for your needs.

runBlocking

The quick among you might guess what this builder does. It blocks the current thread while it executes any suspending functions it contains. It is not a suspending function which means it can be called from normal functions. Primarily used when writing tests or giving demonstrations.

Example
runBlocking signature
                    
    fun waitWhileIDoSomething() = runBlocking {
        System.out.println("Running on ${Thread.currentThread().name}")//main
        launch(Dispatchers.IO) {
            delay(200)
            System.out.println("Now running on ${Thread.currentThread().name}")//worker-1
        }
        System.out.println("And we're back on ${Thread.currentThread().name}")//main
    }
                                        
                    
    fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T
                                        

launch

Used when you want to run a process on a separate thread without a return value. It returns a Job instance which can be used to cancel the job if needed. It is an extension of CoroutineScope so needs one in order to be run.

Example
launch signature
                    
    suspend fun updateUI() = coroutineScope{
        launch { 
            //Update our UI for us
        }   
    }
                                        
                    
    fun CoroutineScope.launch(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    ): Job
                                        

async

Async is used in the case when a return value is needed. The async calls wrap the return value in a Deferred instance which is similar to a Promise or Future from other languages.

It is important to remember to call await() for the results. So important in fact they added a method called withContext to avoid having to call it at all.

Example
async signature
withContext
                    
    suspend fun addNumbers() = coroutineScope{
        val firstSum = async {
            println(Thread.currentThread().name)
            add(2,3)
        }
        val secondSum = async {
            println(Thread.currentThread().name)
            add(27, 14)
        }
        println("total is ${firstSum.await() + secondSum.await()}")
    }
                                        
                    
    public fun <T> CoroutineScope.async(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T>
                                        
                    
    suspend fun addNumbers() = coroutineScope{
        val firstSum = withContext(Dispatchers.IO) {
            println(Thread.currentThread().name)
            add(2,3)
        }
        val secondSum = withContext(Dispatchers.IO) {
            println(Thread.currentThread().name)
            add(27, 14)
        }
        println("total is ${firstSum + secondSum}")
    }
                                        

The coroutineScope builder

In the above examples you will see the use of this builder. This function will wait for all included coroutines to finish which makes it very useful. This is commonly used as part of a pattern called "Structured concurrency" where you run all your coroutines within a context.