MANUEL VIVO: Coroutines
are a future of Kotlin that simplify asynchronous
operations on Android. A new suspend modifier was
introduced in the language, and it is used for
functions that need to be run inside the coroutine. In this video, I
will tell you more about why coroutines
are important and how they work
under the hood. This will help you
better understand why a suspend
function won't return until all the work that it
has started has completed, and also how a
coroutine can suspend without blocking threats. If you learn something new,
like the video and subscribe, but only if you think
we've earned it. [MUSIC PLAYING] Use coroutines to manage
asynchronous tasks that might otherwise
block the main thread and cause your app to freeze. Coroutines are also helpful
to replace callback-based APIs with imperative looking code. Let's see an example
of asynchronous code that uses callbacks. Here, we have the
function, loginUser that, after making an echo
request to get user information from the internet, it saves the
result to the local database and returns the
result of that call-- all of that using callbacks. The result of the
computation is returns using the userResult
callback that is passed in as a
parameter of the function. Code that heavily uses
callbacks can become hard to read and understand. Kotlin coroutines let you
convert callback-based code to sequential code. Code read in sequentially
is typically easier to read and, unlike
callbacks, coroutines provide an easy way to
swap between threads and handle exceptions. See the same function
return with coroutines. We added the suspend
modifier to the function, and now it returns user instead
of having that callback we used to pass in as a parameter. As you can see from the
wiggly icon in the code there, the other functions call from
the suspend function body are also suspend functions. That suspend modifier
tells the compiler that this function needs to be
executed inside a coroutine. As a developer, you can
think of a suspend function as a regular function whose
execution might be suspended and resumed at some point. If you're new to
coroutines in Android and want to learn
more about them, I would recommend going through
the coroutines Codelabs first. But what's the compiler
actually doing under the hood when we mark the
function as suspend? Under the hood,
the Kotlin compiler takes suspended functions
and converts them to an optimized version of
callbacks using a finite state machine. So yes, you are right,
the Kotlin compiler will write those
callbacks for you. There way suspend functions
communicate with each other is with continuation objects. A continuation is just a
generic callback interface with some extra information. Context will be the
CoroutineContext to be used in that continuation. resumeWith resumes
execution of the coroutine with a result that
can contain either a value, which is the result
of the computation that caused the suspension,
or an exception. With Kotlin 1.3, you also have
convenient extension functions called resume and
resumeWithException that are specialized versions
of the resumeWith function. Back to our suspend
function, how is the compiler is
going to modify it? It will replace the
suspend modifier with an extra parameter called
completion of type continuation in the function signature. That will be used to
communicate the result of the suspend
function computation to the coroutine that called
it, as you can see in the code. Also, the return type of
the transformed function is unit instead of user. The user object will be returned
in the added continuation parameter. Time out. As a disclaimer, the
code we are showing will not fully
match the bytecode generated by the compiler. It would be Kotlin
code accurate enough to allow you to
understand what's really happening internally. This representation is generated
by coroutines version 1.3 and might change in future
versions of the library. Back to the code again. The Kotlin compiler will
identify when the function can suspend internally. Every suspension point will
be represented as a state in the finite state machine. And these states are represented
with labels by the compiler. For a better representation
of the state machine, the compiler will use a
when statement to implement the different states. Notice that this
code is incomplete, since the different states have
no way to share information. How is that problem solved? The compiler will use the
continuation parameter to do it. This is why the generic
of the continuation is [INAUDIBLE] any
instead of the return type of the original
function that was user. The compiler will create a
private class that, first, holds the required
data, and second, calls the login user
function recursively to resume the execution of the
function that was suspended. Let's see what that
class looks like. Let's call that generated
class, loginUserStateMachine. It is a private
class that extends from CoroutineImpl, which is
a subtype of continuation. In the constructor, it takes
this continuation object, named Completion, that will
be used to communicate back the result of this function to
the function that called it. This is the same continuation
that we called before in the last state of
the state machine. But also, this class
saves the variables that were declared in the
original suspend function. And there are other
variables that are common for all CoroutineImpls. The result variable
is the result from the previous
continuation and label keeps the state of the state machine. Also, it overrides
the invoke suspend function that is used to
resume the state machine. It will call the
login user function to trigger the
state machine again. It calls it with just
information of the continuation object. The rest of parameters in the
login user function signature become nullable. At that point, label will
be already in the next state to execute, and the result
of the previous state's continuation will be assigned. An instance of this class
is added to login user. The first thing
it needs to do is knowing if it is the first
time the function is called or if the function has
resumed from a previous state. It does it by checking if
the continuation passed in is of type
loginUserStateMachine or not. If it's the first
time, it will create a new loginUserStateMachine
instance and will store the
completion instance received as a parameter. If it's not, it will just carry
on executing the state machine. For completion, this is what
the rest of the function looks like. You can see how the
rest of the code uses the continuation
variable to read the result of the last
state of the state machine. But also, for every
state, it checks if I never happened while
this function was suspended. In the last state,
it calls resume on the continuation of the
function that called this one. And that's it. As you can see,
the Kotlin compiler does a lot under the hood. Because of the implementation
of the generated state machine, a suspend function won't return
until all the work that it has started has completed. How can the code suspend
without blocking the thread? Well, everything
needed to resume the execution of a
suspended function is in the continuation
object that is passed around, so it can
be resumed at any point. That's all we have to say
about suspend for now. Thanks for watching, and go
write better Android apps with Kotlin. [MUSIC PLAYING]