Let’s write a simple async program using CoroutineScope constructor.
fun main() {
CoroutineScope(Dispatchers.Default).launch {
println("Hello")
}
}
The program didn’t print anything even though we created a new scope and launched a coroutine. Why is this so? When using runBlocking
, it works :
fun main() {
runBlocking {
launch {
println("Hello")
}
}
}
The reason behind this must be clear from the name itself - runBlocking
. It runs the input suspend lambda by blocking the control flow. Only after all coroutines inside it have finished, it can return and resume the control flow i.e. it awaits the completion of all its coroutines. This isn’t the case with CoroutineScope builder. The constructor call returns a CoroutineScope object, which is then used to call launch()
function, which is asynchronous and non-blocking function.
runBlocking {
}
CoroutineScope(Dispatchers.Default).launch {
}
runBlocking
is a special scope builder construct, different from all others. Instead of getting the scope object, we pass in the main coroutine lambda to it. runBlocking
handles this root coroutine differently. It awaits the completion of all its child coroutines and then return.
In the following program, main()
function returns before the coroutine finishes because launch()
is a non-blocking function. Thus the program ends and we see no output.
fun main() {
CoroutineScope(Dispatchers.Default).launch {
println("Hello")
}
}
⚠️ When working with console applications, we’ll use runBlocking
instead of any other scope builder. Other scope builders are used in UI applications where program end is triggered by user. Also, runBlocking
shouldn’t be used in UI applications because it is a blocking function.