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
}Colton Idle
08/31/2022, 5:52 PMmkrussel
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
}Colton Idle
08/31/2022, 6:02 PMmkrussel
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 PMColton Idle
08/31/2022, 6:06 PMColton Idle
08/31/2022, 6:06 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 PMColton Idle
08/31/2022, 6:40 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 thecoroutineScopeand multiple uses of thejoinAllcancelling the code joining will not cancel the calls toviewModelScopeor 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 failsFrancesc
08/31/2022, 7:23 PMawaitAll 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