Colton Idle
08/31/2022, 5:35 PMstate.myList.forEachIndexed { index, it ->
viewModelScope.launch {
it.isDone.value = apiService.isDone(it.id)
}
}
I thought I'd be able to add a .await()
to the end of the launch block to know when all of those parrallel api calls are done, but that doesn't compile.
Is there a more idiomatic way to do what I'm trying to do?mkrussel
08/31/2022, 5:43 PMjoinAll
. https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/join-all.htmlEric Chee
08/31/2022, 5:43 PMColton Idle
08/31/2022, 5:51 PMviewModelScope.launch {
state.myList.map { it ->
val job = viewModelScope.launch {
it.isDone.value = apiService.isDone(it.id)
}
job
}.joinAll()
//Do my thing that can only happen after all are done
}
mkrussel
08/31/2022, 5:54 PMcoroutineScope
.
viewModelScope.launch {
coroutineScope {
state.myList.forEachIndexed { index, it ->
launch {
it.isDone.value = apiService.isDone(it.id)
}
}
}
}
Colton Idle
08/31/2022, 5:58 PMmkrussel
08/31/2022, 5:59 PMColton Idle
08/31/2022, 6:00 PMmkrussel
08/31/2022, 6:00 PMColton Idle
08/31/2022, 6:01 PMviewModelScope.launch {
coroutineScope {
state.myList.forEachIndexed { index, it ->
launch {
it.isDone.value = apiService.isDone(it.id)
}
}
}
//Do some work that requires all of the above to be completed first
}
mkrussel
08/31/2022, 6:01 PMColton Idle
08/31/2022, 6:01 PMviewModelScope.launch {
state.myList.map { it ->
val job = viewModelScope.launch {
it.isDone.value = apiService.isDone(it.id)
}
job
}.joinAll()
//Do my thing that can only happen after all are done
}
mkrussel
08/31/2022, 6:03 PMcoroutineScope
creates a structured relationship between the inner jobs and the outer job. With the joinAll
and multiple uses of the viewModelScope
cancelling the code joining will not cancel the calls to isDone
or the updating of isDone.value
.Colton Idle
08/31/2022, 6:05 PMmkrussel
08/31/2022, 6:07 PMColton Idle
08/31/2022, 6:08 PMFrancesc
08/31/2022, 6:28 PMcoroutineScope
approach is likely the simplest and most readable, but you could also use async
instead to map the list to a a list of Deferred and then use awaitAll
on that listColton Idle
08/31/2022, 6:32 PMFrancesc
08/31/2022, 6:33 PMasync
alternative
val deferred = state.myList.map { it ->
viewmodelScope.async {
it.isDone.value = apiService.isDone(it.id)
}
}
deferred.awaitAll()
mkrussel
08/31/2022, 6:34 PMlaunch
and joinAll
would the the same. Not much point in doing async
if you are not using the returned value.Colton Idle
08/31/2022, 6:38 PMviewModelScope.launch {
coroutineScope {
state.myList.forEachIndexed { index, it ->
launch {
it.isDone.value = apiService.isDone(it.id)
}
}
}
//Do some work that requires all of the above to be completed first
}
the //Do some work, didn't get called. hm.Francesc
08/31/2022, 6:39 PMColton Idle
08/31/2022, 6:39 PMmkrussel
08/31/2022, 6:39 PMColton Idle
08/31/2022, 6:39 PMFrancesc
08/31/2022, 6:40 PMColton Idle
08/31/2022, 6:56 PMviewModelScope.launch {
val deferred = state.myList.mapEach { index, it ->
async {
it.isDone.value = apiService.isDone(it.id)
}
}
deferred.awaitAll()
//Do some work that requires all of the above to be completed first
}
@Francesc so this would be your approach correct? I have to wrap the whole thing in a scope.launch{} I believe?Francesc
08/31/2022, 6:57 PMlaunch
for the awaitAll
only, you don't need it for the async
call. And doesn't look like you use the index
so use map
instead of mapEach
. But for your use case, coroutineScope
is simplerColton Idle
08/31/2022, 7:18 PMUsingAny update to your opinion mkrussel? I know its probably vauge. but yeah. with like 3 options. all of em seem pretty darn good. I do like a structured relationship tho 😄creates a structured relationship between the inner jobs and the outer job. With thecoroutineScope
and multiple uses of thejoinAll
cancelling the code joining will not cancel the calls toviewModelScope
or the updating ofisDone
.isDone.value
Francesc
08/31/2022, 7:22 PMlaunch
is for "fire and forget", async
is for when you need the result of your task, and coroutineScope
is helpful when you want to wait for all the children to finish or cancel all when one failsawaitAll
will also fail if any child failsColton Idle
08/31/2022, 7:29 PMFrancesc
08/31/2022, 7:30 PMColton Idle
08/31/2022, 7:31 PMAlex Vanyo
08/31/2022, 7:56 PMviewModelScope.launch {
// ...
viewModelScope.launch {
}
}
where there are nested launch
on some external scope is the most “wrong” in the sense that it starts breaking down structured concurrency in harder to understand ways.
Fire and forget is sometimes what you want, but that makes it more difficult to reason about ordering and cancellation.Colton Idle
08/31/2022, 8:24 PM