
1. Overview
In this example, we will learn about the Kotlin coroutine async-await example.
2. Kotlin Coroutine
A coroutine is a concurrency design pattern you can use to simplify code that executes asynchronously.
It takes a block of code to run, that works concurrently with the rest of the code. However, it is not bound to any thread. It may suspend its execution in one thread and resume in another one.
Kotlin coroutines are extremely inexpensive when compared with threads. Each time when we want to start a new task asynchronously, we can create a new coroutine. To start a new coroutine
, we use one of the main “coroutine builders”: launch
, async
, or runBlocking
.
JetBrains developed the rich kotlinx.coroutines
library as part of version 1.3 for coroutines.
3. Kotlin async coroutine
The async
creates a coroutine and can return the result of the asynchronous task. This returns a reference to the coroutine as a Deferred<T>
whereas T refers to the type of the result. You can use await
or awaitAll
to wait and retrieve the result.
4. Kotlin coroutine async await example
Creates a coroutine and returns its future result as an implementation of Deferred
. Deferred<T>
can return results of type T from the asynchronous task.
fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred<T>
For example, the following async
method returns a string
as output. You can wait for the result using the await
or awaitAll
method.
fun main() = runBlocking() { println("main(): ${Thread.currentThread().name}") val deferred: Deferred<String> = async { println("async: ${Thread.currentThread().name}") println("async: ${Thread.currentThread().name}") "Hello World" } val result = deferred.await() println(result) println("main(): ${Thread.currentThread().name}") } /* prints main(): main @coroutine#1 async corotine: DefaultDispatcher-worker-1 @coroutine#2 async corotine: DefaultDispatcher-worker-1 @coroutine#2 Hello World main(): main @coroutine#1 */
4.1. Cancel the async coroutine
You can cancel the running coroutine by calling the cancel
method on the deferred object.
fun main() = runBlocking() { println("main(): ${Thread.currentThread().name}") val deferred: Deferred<String> = async { println("async: ${Thread.currentThread().name}") println("async: ${Thread.currentThread().name}") "Hello World" } deferred.cancel() println("main(): ${Thread.currentThread().name}") } /* prints main(): main @coroutine#1 main(): main @coroutine#1 */
4.2. Start the async coroutine lazily
By default, the coroutine is immediately scheduled for execution.
However, you can customize the start option by using the start
parameter.
For example, the following launch
coroutine builder contains the start
parameter as CoroutineStart.LAZY to start coroutine lazily. Here, the returned coroutine Deferred
is in the new state initially and later we explicitly started the coroutine by invoking the start
function of the Deferred
.
fun main() = runBlocking() { println("main() method: ${Thread.currentThread().name}") val deferred: Deferred<String> = async(Dispatchers.Default, CoroutineStart.LAZY) { println("async corotine: ${Thread.currentThread().name}") println("async corotine: ${Thread.currentThread().name} ") "Hello World" } deferred.start() val result = deferred.await() println(result) println("main() method on ${Thread.currentThread().name}") } /* prints main() method: main @coroutine#1 async corotine: DefaultDispatcher-worker-1 @coroutine#2 async corotine: DefaultDispatcher-worker-1 @coroutine#2 Hello World main() method on main @coroutine#1 */
4.3. Coroutine async exception
It cancels the parent job (or outer scope) on failure to enforce a structured concurrency paradigm.
For example, the following child async throws an uncaught exception and results in the parent’s cancellation.
fun main() { runBlocking { async { println("parent launch: ${Thread.currentThread().name}") val deferred: Deferred<Unit> = async { println("Launch corotine: ${Thread.currentThread().name}") val i = 100/0 // throw exception println("Launch corotine: ${Thread.currentThread().name}") } deferred.join() println("parent launch: ${Thread.currentThread().name} ") } } } /* prints parent launch: main @coroutine#2 Launch corotine: main @coroutine#3 Exception in thread "main" java.lang.ArithmeticException: / by zero at FileKt$main$1$1$deferred$1.invokeSuspend (File.kt:14) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106) */
To change that behavior, you can use the supervising parent SupervisorJob or supervisorScope. See this article, to learn more about the SupervisorJob.
6. Conclusion
To sum up, we have learned the Kotlin coroutine async await example.