进行多个协程 API 调用并等待所有调用
Posted
技术标签:
【中文标题】进行多个协程 API 调用并等待所有调用【英文标题】:Making multiple coroutine API calls and waiting all of them 【发布时间】:2020-11-03 14:32:50 【问题描述】:所以通常当您必须进行不同的 API 调用并等待时,您会执行以下操作:
viewModelScope.launch
withContext(dispatcherProvider.heavyTasks)
val apiResponse1 = api.get1() //suspend function
val apiResponse2 = api.get2() //suspend function
if (apiResponse1.isSuccessful() && apiResponse2.isSuccessful() ..
但是如果我必须使用不同的参数进行多个并发的相同 API 调用会发生什么:
viewModelScope.launch
withContext(dispatcherProvider.heavyTasks)
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
multipleIds.forEach id ->
val apiResponse1 = api.get1(id) //suspend function
if (apiResponse1.isSuccessful())
content.find it.id == id .enable = true
liveData.postValue(content)
第二种方法的问题是它将遍历multipleIds
列表的所有ID并进行异步调用,但content
可能会在此之前发布。如何等待每个循环的所有响应完成,然后才能查看 postValue
的内容?
【问题讨论】:
也许使用async
并等待主题会有所帮助
【参考方案1】:
确保完成几个异步任务的首选方法是使用coroutineScope
。它将暂停直到所有子作业,例如对launch
或async
的所有调用均已完成。
viewModelScope.launch
withContext(dispatcherProvider.heavyTasks)
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
coroutineScope
multipleIds.forEach id ->
launch // this will allow us to run multiple tasks in parallel
val apiResponse = api.get(id)
if (apiResponse.isSuccessful())
content.find it.id == id .enable = true
// coroutineScope block will wait here until all child tasks are completed
liveData.postValue(content)
如果您对这种相当隐含的方法感到不满意,您还可以使用更实用的方法,使用 async
将您的 id 映射到 Deferred
列表,然后等待它们。这也将允许您并行运行所有任务,但最终会得到按正确顺序排列的结果列表。
viewModelScope.launch
withContext(dispatcherProvider.heavyTasks)
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
val runningTasks = multipleIds.map id ->
async // this will allow us to run multiple tasks in parallel
val apiResponse = api.get(id)
id to apiResponse // associate id and response for later
val responses = runningTasks.awaitAll()
responses.forEach (id, response) ->
if (response.isSuccessful())
content.find it.id == id .enable = true
liveData.postValue(content)
【讨论】:
【参考方案2】:不要使用forEach
,而是使用map
,并在 块内执行相同操作。将映射结果保存到变量并发布此变量。
【讨论】:
【参考方案3】:为了获得并发行为,您需要为每个 id 启动一个新的协程。您可以将multipleIds
和content
移到withContext
块之外。您也可以在withContext
块之后发布结果,因为withContext
是一个暂停函数,因此在其中创建的每个协程都必须在发布结果之前完成。
viewModelScope.launch
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
withContext(dispatcherProvider.heavyTasks)
multipleIds.forEach id ->
launch
val apiResponse = api.get(id) //suspend function
if (apiResponse.isSuccessful())
content.find it.id == id ?.enable = true
liveData.value = content
【讨论】:
当我在 liveData.value 处设置断点时 - 它总是按预期工作,所有列表项都是真的(api.get(id)- 已被调用并得到响应),但是当我运行时没有断点的代码,所有列表项都是错误的(日志显示 api 调用成功),这意味着该响应稍后出现。这不起作用。以上是关于进行多个协程 API 调用并等待所有调用的主要内容,如果未能解决你的问题,请参考以下文章