Итерация сопрограмм и ожидание результатов - PullRequest
0 голосов
/ 02 июля 2019

У меня есть ситуация, когда мне нужно отправить неопределенное количество сетевых вызовов, известных только во время выполнения.Каждый звонок возвращает список.Поскольку каждый возвращается, мне нужно объединить эти списки в один объединенный список.Для этого я использую сопрограммы.

Проблема, с которой я сталкиваюсь, связана с тем, что я не знаю, сколько сетевых вызовов потребуется приложению.Чтобы решить эту проблему, я использую цикл для перебора списка вызовов во время выполнения:

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)
}

В этом коде происходит то, что он входит в первый цикл.Список params правильный.Он отправляет первый запрос, и этот запрос возвращает (я могу видеть это через прокси Чарльза).

Но здесь все просто умирает.Приложение ничего не делает с ответом сети, и цикл завершается (т. Е. Второй итерации цикла нет).

Я знаю, что все остальное не повреждено, потому что у меня есть альтернативная версия, которая не включает в себя цикл.Он просто выполняет два запроса, ожидает их результатов и возвращает объединенный список.Он работает нормально, за исключением того, что он не будет обрабатывать динамическую ситуацию во время выполнения:

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()
}

Возможно, здесь простое решение, но я его не вижу.Любая помощь приветствуется.

1 Ответ

2 голосов
/ 02 июля 2019

Это не правильно:

smallLists[index] = CoroutineScope(Dispatchers.IO).async {
        fetchList(param)
    }

Ваш smallLists пуст, поэтому вы не можете получить доступ к индексу index. Измените это так

smallLists.add(CoroutineScope(Dispatchers.IO).async {
        fetchList(param)
    }
)

Обратите внимание, что вы также можете вызвать awaitAll() в вашем списке async s, чтобы упростить код:

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() }
}
...