The chosen answer is good but fails to address some other important aspects of the sample code that was provided. For instance, launch is non-blocking and is suppose to execute immediately. That is simply not true. The launch itself returns immediately BUT the code inside the launch does appear to be put into a queue and is only executed when any other launches that were previously put into the queue have completed.
Here's a similar piece of sample code with all the delays removed and an additional launch included. Without looking at the result below, see if you can predict the order in which the numbers are printed. Chances are that you will fail:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
coroutineScope {
launch {
println("2")
}
println("3")
}
coroutineScope {
launch {
println("4")
}
println("5")
}
launch {
println("6")
}
for (i in 7..100) {
println(i.toString())
}
println("101")
}
The result is:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
The fact that number 6 is printed last, even after going through nearly 100 println have been executed, indicates that the code inside the last launch never gets executed until all non-blocking code after the launch has completed. But that is not really true either, because if that were the case, the first launch should not have executed until numbers 7 to 101 have completed. Bottom line? Mixing launch and coroutineScope is highly unpredictable and should be avoided if you expect a certain order in the way things should be executed.
To prove that code inside launches is placed into a queue and ONLY executed after ALL the non-blocking code has completed, run this (no coroutineScopes are used):
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
launch {
println("3")
}
for (i in 4..100) {
println(i.toString())
}
println("101")
}
This is the result you get:
4
5
6
...
101
1
2
3
Adding a CoroutineScope will break this behavior. It will cause all non-blocking code that follows the CoroutineScope to not be executed until ALL code prior to the CoroutineScope has completed.
It should also be noted that in this code sample, each of the launches in the queue are executed sequentially in the order that they are added to the queue and each launch will only execute AFTER the previous launch executes. This may make it appear that all launches share a common thread. This is not true. Each of them is given their own thread. However, if any code inside a launch calls a suspend function, the next launch in the queue is started immediately while the suspend function is being carried out. To be honest, this is very strange behavior. Why not just run all the launches in the queue asynchronously? While I don't know the internals of what goes on in this queue, my guess is that each launch in the queue does not get its own thread but all share a common thread. It is only when a suspend function is encountered does it appear that a new thread is created for the next launch in the queue. It may be done this way to save on resources.
To summarize, execution is done in this order:
- Code inside a launch is placed inside a queue and are executed in the order that they are added.
- Non-blocking code following a launch executes immediately before anything in the queue is executed.
- A CoroutineScope blocks ALL code following it BUT will execute all the launch coroutines in the queue before resuming to the code following the CoroutineScope.
coroutineScopeinsightful.... - Roland