Why does the first block not print anything, but t...
# coroutines
d
Why does the first block not print anything, but the second prints "launch launch"?
Copy code
fun main() {
    val scope = CoroutineScope(Job())
    scope.launch { println("launch") }
}
Copy code
fun main() {
    val scope = CoroutineScope(Job())
    scope.launch { println("launch") }
    scope.launch { println("launch") }
}
Is it simply a race condition?
If so, I'm surprised that the second "launch" goes out. Or is it something to do with scope?
j
Most likely yes, a race condition, because nothing waits for your coroutines here. From a
main
method you probably want to use
runBlocking
to launch coroutines (and wait for all of them automatically)
d
I just checked something else; it's probably not a race condition.
Copy code
CoroutineScope(Job()).launch { println("launch") }
    CoroutineScope(Job()).launch { println("launch") }
This prints nothing.
The only difference between it and the other one that prints "launch launch" is that this one doesn't share a scope.
j
It is a consistent and predictable race condition, but still nothing waits for your coroutines here in a guaranteed way. The program can terminate before they even run
d
What you're saying is that the time to run this and only this
CoroutineScope(Job())
is the difference between both blocks printing and neither, 100% of the time across many runs.
I don't buy that
j
Ok let me rephrase, sorry for being unclear. Nothing in the code guarantees anything here. The launch is asynchronous and the program could (in theory) end before the single launch is executed, or both launch in the second example. In practice, the way the dispatcher works seems to be consistent and predictable, but that's an "implementation detail"
d
This prints "launch launch":
Copy code
val scope = CoroutineScope(Job()).also { CoroutineScope(Job()) }
    val scope2 = CoroutineScope(Job())
    scope.launch { println("launch") }
    scope.launch { println("launch") }
This doesn't:
Copy code
val scope = CoroutineScope(Job()).also { CoroutineScope(Job()) }
val scope2 = CoroutineScope(Job())
scope.launch { println("launch") }
scope2.launch { println("launch") }
They should have the exact same runtime
j
Why should they?
d
Why shouldn't they?
j
You don't wait for these coroutines to finish before ending the program so there is no guarantee either way. You shouldn't be able to predict beforehand the behaviour of this code. The fact that it's reproducible 100% of the time is an implementation detail of the dispatchers.
d
The only time difference could come from running
launch
on an unused scope
j
It's not about a time difference
d
So, it doesn't matter because I'm using it wrong, it's probably just something we don't understand
👌 1
j
If you want you can try to manually wait for those coroutines, this would make the code predictable:
Copy code
val scope = CoroutineScope(Job()).also { CoroutineScope(Job()) }
val scope2 = CoroutineScope(Job())
val job = scope.launch { println("launch") }
val job2 = scope2.launch { println("launch") }
runBlocking {
    job.join()
    job2.join()
}
But a more idiomatic way of doing this would be to use `runBlocking`:
Copy code
runBlocking {
    launch { println("launch") }
    launch { println("launch") }
}
d
Thank you, those make sense
I was trying to debug why a button on my Compose ui only fires the
launch
in the click handler the first time
👌 1
So i'll take another crack at it with this in mind