Reputation: 21237
I have a situation where I need to dispatch an indeterminate number of network calls known only at runtime. Each call returns a list. As each is returned, I need to combine these lists in to a single merged list. I am using coroutines to do this.
The problem I am having relates to the fact that I do not know how many network calls the app will need to make. To address this, I am using a loop to iterate over the list of calls at runtime:
private suspend fun fetchData(params: List<Interval>): List<Item> {
val smallLists = mutableListOf<Deferred<List<Item>>>()
val merged = mutableListOf<List<Item>>()
for (index in 0 until params.size) {
val param = params[index]
// loop stop iterating after this call is dispatched
smallLists[index] = CoroutineScope(Dispatchers.IO).async {
fetchList(param)
}
}
for (index in 0 until smallLists.size) {
merged[index] = smallLists[index].await()
}
return merged.flatMap { it.toList() }
}
private fun fetchList(param: Interval) : List<Item> {
return dataSource.fetchData(param)
}
What happens in this code is that it enters the first loop. The params
list is correct. It dispatches the first query, and this query returns (I can see this via a Charles proxy).
But this is where everything just dies. The app does nothing with the network response and the loop terminates (i.e. there is no second iteration of the loop).
I know that everything else is intact because I have an alternate version that does not include looping. It just does two queries, awaits their results, and returns the combined list. It works fine, except that it won't handle a dynamic runtime situation:
private suspend fun fetchData(params: List<Interval>): List<Item> {
val list1 = CoroutineScope(Dispatchers.IO).async {
fetchList(params[0])
}
val list2 = CoroutineScope(Dispatchers.IO).async {
fetchList(params[1])
}
return list1.await() + list2.await()
}
Probably a simple solution here, but I don't see it. Any help is appreciated.
Upvotes: 1
Views: 1008
Reputation: 29260
This is not correct:
smallLists[index] = CoroutineScope(Dispatchers.IO).async {
fetchList(param)
}
Your smallLists
is empty, so you can't access index index
. Change it like this
smallLists.add(CoroutineScope(Dispatchers.IO).async {
fetchList(param)
}
)
Note that you can call awaitAll()
on your list of async
s as well, to simplify your code:
private suspend fun fetchData(params: List<Interval>): List<Item> {
val smallLists = mutableListOf<Deferred<List<Item>>>()
for (index in 0 until params.size) {
val param = params[index]
// loop stop iterating after this call is dispatched
smallLists.add(CoroutineScope(Dispatchers.IO).async {
fetchList(param)
}
})
val merged = smallLists.awaitAll()
return merged.flatMap { it.toList() }
}
Upvotes: 2