Understanding suspend function of Kotlin Coroutines

Elye
Elye
Jan 25, 2018 · 5 min read
Image for post
Image for post

When we talk about coroutine, Suspend Functions are like its backbone. So it is very important to know what it is before one could really appreciate coroutines in full.

However, to understanding what Suspend Functions is, even after reading from various writing found on the internet, it not that straightforward, especially how could it not block the Thread? How is coroutine really different from Thread?

In the Kotlin official coroutines documentation, it is stated

Basically, coroutines are computations that can be suspended without blocking a thread

Image for post
Image for post
Suspended = stop and continue? That sounds like blocking to me!

Hunting to know BLOCKING vs SUSPENDING

I search for blocking vs suspending and found it in English Language Site

A process is blocked when there is some external reason that it can not be restarted, e.g., an I/O device is unavailable, or a semaphore file is locked. A process is suspended means that the OS has stopped executing it, but that could just be for time-slicing (multitasking). There is no implication that the process can not be resumed immediately.

Neither of these words, especially blocked, are being used the same as in non-computer contexts.

Wow, very computer science English Term.

But it is helpful. This provides some cue to it. There’s something about time-slicing and resume immediately.

Illustrating in Diagram

From the cue above, I could imagine the difference between Blocking and Suspending as below

Image for post
Image for post
BLOCKING: Function A has to be completed before Function B continues. The thread is locked for Function A to complete its execution.
Image for post
Image for post
SUSPENDING: Function A, while has started, could be suspended, and let Function B execute, then only resume later. The thread is not locked by Function A.

In short, suspend function is a function that could be started, paused and resume, (and pause and resume…. if wanted repeatedly) and then end.

Let’s talk code, launch vs thread…

Talking about coroutines without code is so intangible. Let’s get into it.

launch as thread

Let’s start with a simple example

fun testRunFunction() {
// Start a coroutine
launch {
println("In start : ${getThreadName()}")
Thread.sleep(200)
println("In ended : ${getThreadName()}")
}

run {
println("Out start: ${getThreadName()}")
Thread.sleep(300)
println("Out ended: ${getThreadName()}")
}
}

The result as below.

Out start: main
In start : ForkJoinPool.commonPool-worker-1
In ended : ForkJoinPool.commonPool-worker-1
Out ended: main
Image for post
Image for post

Great, could do threading… … But wait, can’t I just use Thread{} to do so, since it is launched in a different Thread (i.e. ForkJoinPool.commonPool-worker-1) anyway. Where’s the suspend functionality? So what’s so special about launch?

Forcing launch to run on the same thread

But wait…. didn’t I just draw my suspension function illustration above showing SINGLE THREAD. If I could force launch to run on the same thread, maybe we could see something.

Found there’s a way to do so, by putting it in runBlocking and add coroutineContext as parameter to launch

fun testRunFunction() {
runBlocking {
// Start a coroutine
launch(coroutineContext) {
println("In start : ${getThreadName()}")
Thread.sleep(200)
println("In ended : ${getThreadName()}")
}

run {
println("Out start: ${getThreadName()}")
Thread.sleep(300)
println("Out ended: ${getThreadName()}")
}
}
}

Let’s look at the result

Out start: main
Out ended: main
In start : main
In ended : main

Nice, all are run on main thread!

Hold on… but the In is run after Out. Why?

Well the reason is launch is suspended until the run is completed.

Image for post
Image for post

In the run block, there are no non-blocking functions that allow launch to start its work. There’s now at least some suspension functionality seen.

But, it doesn’t seem very useful if all it does can just be done right after the caller function completed. Let check out further…

Replace sleep with delay

Well, let’s use the special function introduced in Kotlin, i.e. delay() to replace Thread.sleep().

fun testRunFunction() {
runBlocking {
// Start a coroutine
launch(coroutineContext) {
println("In start : ${getThreadName()}")
delay(200)
println("In ended : ${getThreadName()}")
}

run {
println("Out start: ${getThreadName()}")
delay(300)
println("Out ended: ${getThreadName()}")
}
}
}

The result as below

Out start: main
In start : main
In ended : main
Out ended: main

This looks much more interesting now, as In and Out is both mixed up the result. Let’s understand what’s going on with the below diagram.

Image for post
Image for post

From the diagram, we could clearly see that the use of delay() doesn’t block the Thread, but release the Thread for the other coroutine to continue its work, and regain it back as the Thread is released.

This now clearly exhibits what that was mentioned here.

We are using the delay() function that's like Thread.sleep(), but better: it doesn't block a thread, but only suspends the coroutine itself. The thread is returned to the pool while the coroutine is waiting, and when the waiting is done, the coroutine resumes on a free thread in the pool.

Hopefully, this makes it crystal clear the meaning of suspend function that is not blocking the Thread.

Launching on Android UI Thread

As we know we could launch on the same Thread and run thing in parallel, why not let’s try it on the Android Main UI Thread, updating some UI in Parallel.

I wrote a simple App that update 3 different color status bar with some incremental random number, and race to see who reach the final first

Image for post
Image for post

The gist of the code as below

private fun startUpdate() {
resetRun()

greenJob = launch(Android) {
startRunning(progressBarGreen)
}

redJob = launch(Android) {
startRunning(progressBarRed)
}

blueJob =launch(Android) {
startRunning(progressBarBlue)
}
}

private suspend fun startRunning(
progressBar: RoundCornerProgressBar) {
progressBar.progress = 0f
while (progressBar.progress < 1000 && !raceEnd) {
delay(10)
progressBar.progress += (1..10).random()
}
if (!raceEnd) {
raceEnd = true
Toast.makeText(this, "${progressBar.tooltipText} won!",
Toast.LENGTH_SHORT).show()
}
}

Here you could see there are three jobs got launched. And all calling the same function to update their respective progress bar. The bar got updated in a seemingly parallel fashion. All done in the Main UI Thread without spawning other threads. Amazing!

Check out the code in


Thanks for reading. You can check out my other topics here.

You can follow me on Medium, Twitter, Facebook, and Reddit for little tips and learning on mobile development, medium writing, etc related topics. ~Elye~

Mobile App Development Publication

Sharing Mobile App Development and Learning

Sign up for Update from Mobile App Development Publication

By Mobile App Development Publication

A place where we learn and share our mobile app development experience on Medium Learn more

Create a free Medium account to get Update from Mobile App Development Publication in your inbox.

Elye

Written by

Elye

Passionate about learning, and sharing mobile development and others https://twitter.com/elye_project https://www.facebook.com/elye.proj

Mobile App Development Publication

Sharing iOS, Android and relevant Mobile App Development Technology and Learning

Elye

Written by

Elye

Passionate about learning, and sharing mobile development and others https://twitter.com/elye_project https://www.facebook.com/elye.proj

Mobile App Development Publication

Sharing iOS, Android and relevant Mobile App Development Technology and Learning

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store