使用协程的并行请求
Parallel requests with coroutines
我正在尝试从多个位置获取一些数据来填充 recyclerView。我以前使用回调,效果很好,但需要重构为协程。
所以我有一个改造服务列表,并并行调用它们中的每一个。然后我可以使用 onResponse 回调更新 recyclerView。我怎样才能用协程实现这个。
我试过类似的方法,但是下一个调用在我得到响应后被触发:
runblocking {
for (service in services) {
val response = async(Dispatchers.IO) {
service.getResponseAsync()
}
adapter.updateRecyclerView(response.await())
}
}
使用另一种方法时,我遇到了无法返回主线程更新我的 ui 的问题,因为我正在使用启动并且无法等待响应:
runblocking {
services.foreach {
launch(Dispatcher.IO) {
val response = it.getResponseAsync()
}
withContext(Dispatcher.Main) {
adapter.updateRecyclerView(response)
}
}
}
我很感谢你给我的每一个提示 ;)
干杯帕特里克
使用 launch
而不是 runBlocking
启动协程。下面的示例假设您从默认使用 Dispatchers.Main
的上下文启动。如果不是这种情况,您可以对这些使用 launch(Dispatchers.Main)
。
如果您想在每次任何并行操作 returns 时更新您的视图,请将您的 UI 更新移动到您为每个 [=16= 启动的协程中] 项:
for (service in services) {
launch {
val response = withContext(Dispatchers.IO) { service.getResponseAsync() }
adapter.updateRecyclerView(response)
}
}
如果您只需要在它们全部返回后更新,您可以使用awaitAll
。在这里,您的 updateRecyclerView
函数必须编写为处理一系列响应,而不是一次处理一个响应。
launch {
val responses = services.map { service ->
async(Dispatchers.IO) { service.getResponseAsync() }
}
adapter.updateRecyclerView(responses.awaitAll())
}
await()
调用挂起当前协程并释放当前线程以供其他排队的协程附加。
因此,当调用 await()
时,当前协程会挂起,直到收到响应,这就是 for 循环未完成的原因(在 before 请求完成之前进入下一次迭代)。
首先,您不应该在这里使用 runBlocking
,强烈建议不要在生产环境中使用它。
您应该改为使用 android 提供的 ViewModel 范围进行结构化并发(如果不再需要则取消请求,例如 activity 的生命周期结束)。
您可以在 activity 或片段 viewModelOwner.viewModelScope.launch(/*Other dispatcher if needed*/) {}
中使用像这样的视图模型范围,或者自己制作一个协程范围,并附加一个在 onDestroy 时自行取消的作业。
针对协程不并行请求的问题,可以在for循环中不await(ing)发起多个请求。
和select他们,使用select表达式https://kotlinlang.org/docs/reference/coroutines/select-expression.html#selecting-deferred-values
示例:
viewModelOwner.viewModelScope.launch {
val responses = mutableListOf<Deferred<TypeReturnedFromGetResponse>>()
for (service in services) {
async(Dispatchers.IO) {
service.getResponseAsync()
}.let(responses::add)
}
// adds which ever request is done first in oppose to awaiting for all then update
for (i in responses.indices) {
select<Unit> {
for (response in responses) {
response.onAwait {
adapter.updateRecyclerView(it)
}
}
}
}
}
PS:使用此方法看起来很丑陋,但会在首先解决任何请求后立即更新适配器,而不是等待每个请求然后更新其中的项目.
我正在尝试从多个位置获取一些数据来填充 recyclerView。我以前使用回调,效果很好,但需要重构为协程。
所以我有一个改造服务列表,并并行调用它们中的每一个。然后我可以使用 onResponse 回调更新 recyclerView。我怎样才能用协程实现这个。
我试过类似的方法,但是下一个调用在我得到响应后被触发:
runblocking {
for (service in services) {
val response = async(Dispatchers.IO) {
service.getResponseAsync()
}
adapter.updateRecyclerView(response.await())
}
}
使用另一种方法时,我遇到了无法返回主线程更新我的 ui 的问题,因为我正在使用启动并且无法等待响应:
runblocking {
services.foreach {
launch(Dispatcher.IO) {
val response = it.getResponseAsync()
}
withContext(Dispatcher.Main) {
adapter.updateRecyclerView(response)
}
}
}
我很感谢你给我的每一个提示 ;) 干杯帕特里克
使用 launch
而不是 runBlocking
启动协程。下面的示例假设您从默认使用 Dispatchers.Main
的上下文启动。如果不是这种情况,您可以对这些使用 launch(Dispatchers.Main)
。
如果您想在每次任何并行操作 returns 时更新您的视图,请将您的 UI 更新移动到您为每个 [=16= 启动的协程中] 项:
for (service in services) {
launch {
val response = withContext(Dispatchers.IO) { service.getResponseAsync() }
adapter.updateRecyclerView(response)
}
}
如果您只需要在它们全部返回后更新,您可以使用awaitAll
。在这里,您的 updateRecyclerView
函数必须编写为处理一系列响应,而不是一次处理一个响应。
launch {
val responses = services.map { service ->
async(Dispatchers.IO) { service.getResponseAsync() }
}
adapter.updateRecyclerView(responses.awaitAll())
}
await()
调用挂起当前协程并释放当前线程以供其他排队的协程附加。
因此,当调用 await()
时,当前协程会挂起,直到收到响应,这就是 for 循环未完成的原因(在 before 请求完成之前进入下一次迭代)。
首先,您不应该在这里使用 runBlocking
,强烈建议不要在生产环境中使用它。
您应该改为使用 android 提供的 ViewModel 范围进行结构化并发(如果不再需要则取消请求,例如 activity 的生命周期结束)。
您可以在 activity 或片段 viewModelOwner.viewModelScope.launch(/*Other dispatcher if needed*/) {}
中使用像这样的视图模型范围,或者自己制作一个协程范围,并附加一个在 onDestroy 时自行取消的作业。
针对协程不并行请求的问题,可以在for循环中不await(ing)发起多个请求。
和select他们,使用select表达式https://kotlinlang.org/docs/reference/coroutines/select-expression.html#selecting-deferred-values
示例:
viewModelOwner.viewModelScope.launch {
val responses = mutableListOf<Deferred<TypeReturnedFromGetResponse>>()
for (service in services) {
async(Dispatchers.IO) {
service.getResponseAsync()
}.let(responses::add)
}
// adds which ever request is done first in oppose to awaiting for all then update
for (i in responses.indices) {
select<Unit> {
for (response in responses) {
response.onAwait {
adapter.updateRecyclerView(it)
}
}
}
}
}
PS:使用此方法看起来很丑陋,但会在首先解决任何请求后立即更新适配器,而不是等待每个请求然后更新其中的项目.