https://kotlinlang.org logo
#coroutines
Title
# coroutines
c

Colton Idle

04/30/2020, 8:29 PM
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

octylFractal

04/30/2020, 8:30 PM
that looks fine, but you may need a scope to call
async
, in which case you should wrap that whole block with
coroutineScope {}
c

Colton Idle

04/30/2020, 8:32 PM
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

octylFractal

04/30/2020, 8:32 PM
okay, that's fine then
c

Colton Idle

04/30/2020, 8:32 PM
Updated my question to include that just so that I'm clear.
@octylFractal thats awesome. Crazy how easy that is.
o

octylFractal

04/30/2020, 8:35 PM
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

Colton Idle

04/30/2020, 8:37 PM
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

octylFractal

04/30/2020, 8:37 PM
isn't that basically what you're doing?
c

Colton Idle

04/30/2020, 8:39 PM
No? Unless I'm missing something here, but it looks completely different! 😄
o

octylFractal

04/30/2020, 8:40 PM
`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

Colton Idle

04/30/2020, 8:47 PM
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

octylFractal

04/30/2020, 8:49 PM
well, you could, but you wouldn't get any values back
so it wouldn't be very useful for retrieving data
c

Colton Idle

04/30/2020, 8:49 PM
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

octylFractal

04/30/2020, 8:51 PM
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

Colton Idle

04/30/2020, 8:52 PM
Cool. I mostly want to learn how to spot parallel code. So it looks like await will likely be the dead giveaway
o

octylFractal

04/30/2020, 8:53 PM
anything that uses the CoroutineScope basically
this includes
CoroutineScope.launch
,
CoroutineScope.produce
,
Flow.launchIn
,
CoroutineScope.actor
, etc.
c

Colton Idle

04/30/2020, 8:53 PM
Thank you @octylFractal
e

EyeCon

05/02/2020, 5:15 PM
@Colton Idle maybe this setting would be helpful.
This is what I use. It subtly hints to suspending functions.
o

octylFractal

05/02/2020, 5:18 PM
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

EyeCon

05/02/2020, 8:59 PM
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.