Deep look into Coroutine Suspend Functions. Part 2. How does Continuation work

Dmitry Pliskov
Axel Springer Tech
Published in
3 min readFeb 21, 2020

The series consists of 5 parts:

1. Suspend functions introduction
2. How does Continuation work
3. Get result from suspend function
4. How to create a suspend function
5. A small FAQ about suspend functions

In the first part we learned that suspend functions may suspend execution of the code without blocking the thread of execution. In this part we will explore how this is possible.

Let’s have a look at this coroutine code:

Function download is a suspend function that downloads a file. Later we will learn how to create suspend functions. For now, it’s enough to know that this function doesn’t block the thread because it uses a background thread for downloading. Even though the download function will run in background, the toast will only show up once this background function has finished. This is possible thanks to the Continuation mechanism.

As you know Kotlin code will be transformed to Java classes. For coroutines there is a separate Java class that is called Continuation. Let’s have a look at this class. Since its implementation is very complex we start with a simplified version and add more and more logic to it step by step. This approach will help us to understand Continuation better.

A very simplified version of Continuation for the coroutine above looks like this:

The method invokeSuspend contains code from the coroutine and is called when the coroutine starts.

Because under the hood Kotlin coroutines are implemented using Java, there are no coroutines or suspend functions available. Instead, there are just an invokeSuspend method and an asynchronous function download.

The main goal of the Continuation is to call the toast method after the completion of the download method. This could be achieved via a callback, but in the Java code of the coroutine, the code is split into two parts and a label (“0” and “1”) is added to each.

The dividing point here is the suspend function. The function itself and the code before is placed in the first case of the switch statement. The code after the suspend function is placed in the second case.

Which part is run when invokeSuspend is called depends on the variable label. The first part is run if label is 0 and second — if label is 1. This is how the calls for download and toast function are separated

The method invokeSuspend will be called the first time when the coroutine starts. During this call the first part (case 0) will be run and suspend function download will start its asynchronous work. Afterwards, invokeSuspend will be finished.

When suspend function is done, invokeSuspend is called again, to run the second part of the coroutine (the toast methods). To enable this, the value of label needs to be changed to 1. This is implemented in the first part of the switch statement, before calling suspend function:

The second call of invokeSuspend with label “1” could be done by the suspend function download after finishing the execution.

When the suspend function is transformed from Kotlin to Java, it gets an extra input parameter with the interface type Continuation. The class we are currently looking at implements this interface, so we can refer to it as “this” within the code and pass to the suspend function.

When the suspend function finishes its work it will call invokeSuspend of the Continuation object we passed into it. label was changed to 1 (before calling suspend function), therefore the switch statement will run the second part of the code (case 1) and method toast will be called.

So, Continuation is like a callback for suspend function. If there are more than one suspend functions in one coroutine, Continuation will be the callback for all of them. In one of the next parts we will have a look at an extended example with 2 suspend functions.

For more details, feel free to watch the following video, showing the whole coroutine running process.

In the next part, we will see how the suspend function can return a value to Continuation.

--

--