Kotlin: Coroutines - builders
30th December 2021
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.
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.
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.
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.