This is the third part of my series, so make sure you check out the first two articles!
P.S. If you’ve already read them, I’ve updated them with a bit of Kotlin code, to better explain certain concepts, so I invite you to check them out again! :]
- Asynchronous programming with Kotlin and Coroutines— Lesson 1: Routines
- Asynchronous programming with Kotlin and Coroutines — Lesson 2: Suspension Points
This series has been all about asynchronous programming and coroutines. And to fully understand the whys and hows of these topics, we need to elaborate several computer science concepts that make up the foundation of both.
So far, we learned what routines are, how they represent the lifecycle and family-tree-like relations between called functions. We’ve also talked about suspension points in code, and how code can have both positive and negative execution flows, or happy paths and exceptions, respectively.
Then again it’s an important thing to understand how code is being executed at all, where the state of current execution flow lives, and how the main program knows where to proceed after some routine is finished.
Continuing (pun not intended) where we left off, using everyday examples, we’ll be explaining continuation constructs, or how programs and computers know where they left off, where to proceed with execution, and which state they were in last.
When a function is called, or more generally speaking a routine is started, it creates a construct called a continuation. It’s reified program control state, which also knows about where it’s been created or started (where in the code), and it has general knowledge of the current program. It doesn’t know all the details about the program, as it would be too much overhead.
It serves as a portal to the last known location in code, before the function was called. Since it knows where it’s been created (where the routine started) it can return there at any point, effectively resuming the execution.
If you’re getting flashbacks to callbacks, you’re on the right track. They’re a cleaner form of callbacks, which doesn’t redirect you further down the code, but continues with the execution of the current function.
In the second part of the series we explained suspension points. Their physical representation would be the lines of code at which they occur. As the program maps function calls to machine code by the line number and the names that define said functions.This is also how suspension points are tied to continuations. They’re actually pretty dependant on each other.
When a suspension point is reached, the routine is either started with a new continuation object, holding the context in which it was started, or it’s finished by calling back to the point of origin, using the starting context to proceed with execution.
So what a computer does is map the calls and some of it’s state to a continuation object, pass it on to the next function, where the continuation serves sort of like a map back home, giving the function the opportunity to go back anytime it’s finished. Continuations, as mentioned before, are like a very low-level callback.
All this tech talk is making me hungry… And since I’m a sugar addict, I keep thinking about those cookies from the second article, and how continuations fit in there. You can think of it like this, when you’re at home, you have one parent routine which we can call Hanging around (HA for short). As soon as you get the idea of eating cookies, you may start a Baking (B for short) routine, and store away the HA with the idea of eating cookies.
The general state of HA is also passed inside a continuation to the new routine. If by any chance you don’t have the necessary ingredients or your oven is broken, by knowing where you last left off before starting B, you can return to hanging around the house and craving cookies without any excess work. In programming, this is done by either returning null, an empty value, or throwing an exception. Sadly, this means you don’t have any cookies. :[
On the other hand, if you manage to make the cookies and put them in the oven, again, by knowing where you left off in HA, you can return and hang around the house, possibly starting some other routine, until the cookies are baked. When they’re baked, you go back to the idea of a cookie craving, but at this point, you’ve already got some cookies!
Now in programming this is done using asynchronous programming, by calling a function and passing in a callback, like in the second part of the series. You pass the oven a callback (the ringer on the oven itself) which notifies you of cookies being done, and you proceed to do something else while waiting.
Later in the series, we’ll show how, using coroutines, we can omit the callback, anticipate the value in the future, and program sequential-like code, which is then executed asynchronously.
Continuations work just like a small, computer science, time-travel devices, giving you access back to the point where you last left off and called some code. Only, by then, you either have a value processed and computed, or your program ended with an error. They are a powerful mechanism computers use to hop around your code and possibly work on multiple things in parallel.
In the case of asynchronous programming and coroutines, they form the basis of both concepts. They become available as types and objects which can be used to explicitly change, resume, or conclude the execution flow of functions.