Can you please explain me what is the difference between these two blocks of code. First time prints 421, but second prints 606. Why first one is parallel and second one is sequential?
fun main(args: Array<String>) = runBlocking {
var time = measureTimeMillis {
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()
println(int1 + int2)
}
println(time)
time = measureTimeMillis {
val one = async { one() }.await()
val two = async { two() }.await()
println(one + two)
}
print(time)
}
suspend fun one(): Int {
delay(200)
return 12
}
suspend fun two(): Int {
delay(400)
return 23
}
by default the methods are executed sequentially one after another. N.B:- codes are sequentially executed within a coroutine by default.
We can wait for the coroutine to finish by calling join() on the Job. For example, suppose we have a suspend function to download some files. We can launch this coroutine and capture the resulting job, which we can later use to join — to wait for the operation to complete.
Note: The AsyncTask class was deprecated in Android 11. Kotlin coroutines are now the recommended solution for async code.
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()
What this does:
val one = async { one() }.await()
val two = async { two() }.await()
What this does:
There's no concurrency here, it's purely sequential code. In fact, for sequential execution you shouldn't even use async
. The proper idiom is
val one = withContext(Dispatchers.Default) { one() }
val two = withContext(Dispatchers.Default) { two() }
In the first variant you get a Deferred<Int>
for both async-calls. As the documentation of Deferred
shows so nicely there are several states the deferred object might be in. From the outside that state is now either new
or active
but surely not completed yet. On your second variant however the first async-await needs a completed
state already otherwise you could not have any value there. However on your async{one()}.await()
the second async
is not known yet.
Note also that the return value of await()
is now Int
and not Deferred
anymore, so the coroutine must have been executed by then. Check also the documentation of await()
.
In other words:
val one = async { one() }
val two = async { two() }
Both one
and two
are now Deferred<Int>
. None has been called yet (or might have been called yet). As soon as you call one.await()
it may start already both one
and two
, just because it has the resources for it (even if you didn't use two.await()
anywhere in your code).
On the second variant however:
val one = async { one() }.await()
val two = async { two() }.await()
Even though it creates a coroutine for async {one()}
it must set a value to one
immediately, because you are calling await()
on it. The types of one
and two
are both Int
. So as soon as the first of those lines is hit, the async code needs to be executed immediately. By then nobody knows that another asynchronous call has to be executed as we wait for the value of the first. If the first wouldn't have an await
, the coroutine would again be executed in parallel, e.g.:
val one = async { one() }
val two = async { two() }.await()
will execute one()
and two()
in parallel.
So maybe this can be summarized to: only those coroutines can be executed in parallel on an await, that are known/spawned by then.
The thumb-rules:
Check this for more details
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With