I've got 2 coroutines making a network call. call ...
# coroutines
c
I've got 2 coroutines making a network call. call A and call B. I want to know 3 things: 1. When a completes 2. When b completes 3. When both a AND b completes is there an easy way to do this with coroutines? I know I can use awaitAll to wait for option 3, but I also want to do stuff when A and B are done separately.
My "use case" I have a screen where I want to show results of network call A (a song title) and then I have network call B which is the artist name for the song. Doing that is pretty easy. Call both network calls in parrallel and whichever one of them is done, then I just show that info on the screen. But the issue is that then I also have a last thing that I want to show on the screen ONLY when getting the song title + artist name becomes available. I can only really think of doing this with some saving some field of like networkCallADone and networkCallBDone and I would check both of those fields after both network call A was done and after network call B was done. This seems inefficient because I might have to add a 3rd network call in the future.
f
If you want to stay at high level (which I suggest) you can use flows, like this: https://pl.kotl.in/dYXD-CtnQ If you want to go lower level, you can use the select statement directly: https://pl.kotl.in/8xXplrEiz
j
Why not simply:
Copy code
coroutineScope {
    launch {
        val title = fetchTitle()
        showTitle(title)
    }
    launch {
        val artist = fetchArtist()
        showArtist(artist)
    }
}
showThingAfterBoth()
c
lol
🙈
can it be that simple? maybe i need more sleep. but yeah that looks right, except that showThingAfterBoth() needs title and artist to be passed in.
j
If you need the title and artist, you can turn the `launch`es into `async`s instead, it would still be rather readable, albeit slightly more complicated
c
hm. Let me see if I can figure that out once im back at my computer.
j
I meant something like:
Copy code
val (title, artist) = coroutineScope {
    val deferredTitle = async {
        val title = fetchTitle()
        showTitle(title)
        title
    }
    val deferredArtist = launch {
        val artist = fetchArtist()
        showArtist(artist)
        artist
    }
    deferredTitle.await() to deferredArtist.await()
}
showThingAfterBoth(title, artist)
It's not terribly beautiful, but you get the idea.
p
for this case I use this second if you really want to know all these states with async 🙂 not sure it's the best
j
Yeah depending on the number of async and data you need out of those coroutines, a
Pair
definitely isn't the best, for instance. Probably a proper data class becomes necessary pretty quickly.
p
this is also the case of withContext also all of them have different use case scenarios
u
I’d actually suggest to use flows and emit enums/sealed classes to signal what’s going on. And then move your UI updates to
collect
j
Yeah I'd probably prefer this approach too