Coroutines in Kotlin

ISOP
ISOP Nepal
Published in
3 min readMay 24, 2019

--

In Mobile App, it’s essential to avoid blocking the main thread.

The main thread is a single thread that handles all updates to the UI. It’s also the thread that calls all click handlers and other UI callbacks. As such, it has to run smoothly to guarantee great user experience.

Many common tasks like parsing large JSON datasets, writing data to a database, or fetching data from the network takes longer time. Therefore, calling code from the main thread can cause the app to freeze. And if you block the main thread for too long, the app may even crash and present an Application Not Responding dialog.

One common example of performing a long-running task without blocking the main thread is callbacks pattern. By utilizing callbacks, we can begin a long-running task on a background thread. When the task completes, the callback is called to inform you of the result on the main thread.

Here is the Example of callback pattern:

// Slow request with callbacks
@UiThread
fun performNetworkRequest() {
// The slow network request runs on another thread
slowFetch { result ->
// When the result is ready, this callback will get the result
show(result)
}
// performNetworkRequest() exits after calling slowFetch without waiting for the result
}

This code is annotated with, @UiThread it must run fast enough to execute on the main thread. That means it needs to return very quickly so that the next screen update is not delayed. However, since slowFetch will take seconds or even minutes to complete, the main thread can't wait for the result. The show(result) callback allows slowFetch to run on a background thread and return the result when it's ready.

Remove Callbacks Using Coroutines

Kotlin coroutines let you convert callback-based code to sequential code. Code written sequentially is typically easier to read, and can even use language features such as exceptions.

In the end, they do the exact same thing: wait until a result is available from a long-running task and continue execution.

The keyword suspend is Kotlin's way of marking a function, or function type, available to coroutines. When a coroutine calls a function marked suspend, instead of blocking until that function returns like a normal function call, it suspends execution until the result is ready then it resumes where it left off with the result. While it's suspended waiting for a result, it unblocks the thread that it's running on so other functions or coroutines can run.

Callbacks are a great pattern, however, they have a few drawbacks. Code that heavily uses callbacks can become hard to read and harder to reason about. In addition, callbacks don’t allow the use of some language features, such as exceptions.

// Slow request with coroutines
@UiThread
suspend fun performNetworkRequest() {
// slowFetch is another suspend function so instead of
// blocking the main thread performNetworkRequest will ` . suspend` until the result is
// ready
val result = slowFetch()
// continue to execute after the result is ready
show(result)
}

// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }

The suspend keyword doesn't specify the thread code runs on. Suspend functions may run on a background thread or the main thread.

Compared to the callback-based code, coroutine code accomplishes the same result of unblocking the current thread with less code. Due to its sequential style, it’s easy to chain several long-running tasks without creating multiple callbacks. For example, code that fetches a result from two network endpoints and saves it to the database can be written as a function in coroutines with no callbacks.

// Request data from network and save it to database with coroutines

// Because of the @WorkerThread, this function cannot be called on the
// main thread without causing an error.
@WorkerThread
suspend fun performNetworkRequest() {
// slowFetch and anotherFetch are suspend functions
val slow = slowFetch()
val another = anotherFetch()
// save is a regular function and will block this thread
database.save(slow, another)
}

// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
// anotherFetch is main-safe using coroutines
suspend fun anotherFetch(): AnotherResult { ... }

The pattern of async and await in other languages is based on coroutines. If you're familiar with this pattern, the suspend keyword is similar to async. However, in Kotlin, await() is implicit when calling a suspendfunction.

Kotlin has a method Deferred.await() that is used to wait for the result from a coroutine started with the async builder.

--

--

ISOP
ISOP Nepal

ISOP app is your virtual classroom. Virtual classroom allows you to learn when you like, not when bell rings.