I\'m reading Kotlin Coroutine and know that it is based on suspend
function. But what does suspend
mean?
Coroutine or function gets
There are a lot of great answers here, but I think there are two additional things that are important to note.
launch / withContext / runBlocking and a lot of other things in the examples are from the coroutines library. which actually have nothing to do with suspend. you don't need the coroutines library to use coroutines. Coroutines are a compiler "trick". Yes, the library sure makes things easier, but the compiler is doing the magic of suspending & resuming things.
The second thing, is the compiler is just taking code that looks procedural and turning it into callbacks under the hood.
Take the following minimal coroutine that suspends that does not use the coroutine library :
lateinit var context: Continuation
suspend {
val extra="extra"
println("before suspend $extra")
suspendCoroutine { context = it }
println("after suspend $extra")
}.startCoroutine(
object : Continuation {
override val context: CoroutineContext = EmptyCoroutineContext
// called when a coroutine ends. do nothing.
override fun resumeWith(result: Result) {
result.onFailure { ex : Throwable -> throw ex }
}
}
)
println("kick it")
context.resume(Unit)
I think an important way to understand it is to look at what the compiler does with this code. effecticvly it creates a class for the lamba. it creates a property in the class for the "extra" string, then it creates two function, one that prints the "before" and another the prints the "after".
effectivly the compuler took what looks like prodecrual code and turned it into callbacks.
so what does the "suspend" keyword do? It tell the compiler how far back to look for context that the generated callbacks will need. The compiler needs to know which variables are used in which "callbacks", and the suspend keyword help it. In this example the "extra" variable is used both before and after the suspend. so it needs to be pulled out to a properity of the class containing the callbacks the compiler makes.
It also tells the compiler that this is the "beginning" of state and to prepare to split up the following code into callbacks. The "startCourtine" only exists on suspend lambda.
The actual java code generate by the kotlin compiler is here. It's a switch statement instead of calls backs, but it's effectively the same thing. called first w/ case 0, then w/ case 1 after the resume.
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
var10_2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0: {
ResultKt.throwOnFailure((Object)$result);
extra = "extra";
var3_4 = "before delay " + extra;
var4_9 = false;
System.out.println((Object)var3_4);
var3_5 = this;
var4_9 = false;
var5_10 = false;
this.L$0 = extra;
this.L$1 = var3_5;
this.label = 1;
var5_11 = var3_5;
var6_12 = false;
var7_13 = new SafeContinuation(IntrinsicsKt.intercepted((Continuation)var5_11));
it = (Continuation)var7_13;
$i$a$-suspendCoroutine-AppKt$main$1$1 = false;
this.$context.element = it;
v0 = var7_13.getOrThrow();
if (v0 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended((Continuation)var3_5);
}
v1 = v0;
if (v0 == var10_2) {
return var10_2;
}
** GOTO lbl33
}
case 1: {
var3_6 = this.L$1;
extra = (String)this.L$0;
ResultKt.throwOnFailure((Object)$result);
v1 = $result;
lbl33:
// 2 sources
var3_8 = "after suspend " + extra;
var4_9 = false;
System.out.println((Object)var3_8);
return Unit.INSTANCE;
}
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}