Trying to sanity check converting some coroutines ...
# coroutines
c
Trying to sanity check converting some coroutines retrofit code to something that runs in parallel but stills waits for both calls to be complete. This is my sequential coroutine code
Copy code
viewModelScope.launch {

val nameResponse = retrofitService.getName()
val phoneResponse = retrofitService.getPhone()
print("${nameResponse.isSuccessful.toString()} ${phoneResponse.isSuccessful.toString()}")

}
Is making it parallel as easy as doing this?
Copy code
viewModelScope.launch {

val nameResponse = async { retrofitService.getName() }
val phoneResponse = async { retrofitService.getPhone() }
print("${nameResponse.await().isSuccessful.toString()} ${phoneResponse.await().isSuccessful.toString()}")

}
If that is all I need. Am I missing something anything from your point of view. Besides error handling (which is a whole other topic)? Any other ways to do this?
o
that looks fine, but you may need a scope to call
async
, in which case you should wrap that whole block with
coroutineScope {}
c
So I'm doing this on android in a VM, which seems to have it's own scope I can use. So I am launching everything in
Copy code
viewModelScope.launch {
//my code here
}
o
okay, that's fine then
c
Updated my question to include that just so that I'm clear.
@octylFractal thats awesome. Crazy how easy that is.
o
I do want to note, if
retrofitService.getName()
isn't suspending and can block for long enough to consider it not-good for the view thread, you should
async(<http://Dispatchers.IO|Dispatchers.IO>)
instead
but if it does suspend, then you don't need to worry about that
👍 1
oh, but if it's doing something blocking but CPU-bound,
async(Dispatchers.Default)
is actually the correct thing. obviously the IO dispatcher is just for IO (disk/network/etc.)
👍 1
c
makes sense. It does suspend. I originally stumbled upon this answer from Roman and I thought that maybe this is how I have to do parallel calls. https://stackoverflow.com/questions/54854187/kotlin-coroutines-waiting-for-multiple-threads-to-finish
o
isn't that basically what you're doing?
c
No? Unless I'm missing something here, but it looks completely different! 😄
o
`launch`/`async` are very similar, the only difference being that
async
allows access to the value at a later point.
withContext
is just another way to (1) switch dispatchers, and (2) open up a new CoroutineScope like I suggested with
coroutineScope {}
c
Aha. Thanks for the details
So I could do something like this?
Copy code
suspend fun getNameAndPhone() = withContext(<http://Dispatchers.IO|Dispatchers.IO>) { 
    // withContext waits for all children coroutines 
    launch { retrofitService.getName() }
    launch { retrofitService.getPhone() }
}
o
well, you could, but you wouldn't get any values back
so it wouldn't be very useful for retrieving data
c
So basically there's no other real way to write this code differently?
Copy code
viewModelScope.launch {

//Call in parralel
val nameRes = async { retrofitService.getName() }
val phoneRes = async { retrofitService.getPhone() }

//Wait for both to be done
print("${nameRes.await().isSuccessful.toString()} ${phoneRes.await().isSuccessful.toString()}")

}
o
there are ways to refactor it slightly differently, but they will all do the same thing e.g., you could do
Copy code
suspend fun getNameAndPhone() = coroutineScope {
  val nameRes = async { retrofitService.getName() }
  val phoneRes = async { retrofitService.getPhone() }
  return@corountineScope nameRes.await() to phoneRes.await()
}
which would get you a
Pair<Name, Phone>
(or whatever the type is)
👍 1
c
Cool. I mostly want to learn how to spot parallel code. So it looks like await will likely be the dead giveaway
o
anything that uses the CoroutineScope basically
this includes
CoroutineScope.launch
,
CoroutineScope.produce
,
Flow.launchIn
,
CoroutineScope.actor
, etc.
c
Thank you @octylFractal
e
@Colton Idle maybe this setting would be helpful.
This is what I use. It subtly hints to suspending functions.
o
Doesn't intellij also indicate them in the gutter on the left of the line numbers? I suppose it can get confusing in one-liners, but it's generally sufficient
e
It does, you are right. It even indicates that if you use a
for
on a channel for example. But for lambdas and such, this is a quick way to see what exactly is suspending.