我正在慢慢地学习kotlin-coroutines,我想从简单的情况开始,在这种情况下,我遍历列表,每次调用一个API调用,等待响应,处理它,然后每次迭代都重复这个过程。
viewModelScope.launch {
list.forEach { item ->
Log.d("TESTING", "forEach iteration before calling test(): ${item.id}")
test()
Log.d("TESTING", "forEach iteration after calling test(): ${item.id}")
}
}
suspend fun test(item.id: String) = coroutineScope {
Log.d("TESTING", "suspend test before receiving response: ${item.id}")
val apiRepsonse = async {
repository.doingApiCall() {
Log.d("TESTING", "response: ${item.id}")
}
}.await()
Log.d("TESTING", "suspend test after receiving response: ${item.id}")
}所以,如果我们想象一下,list包含:{ 10, 15, 25, 35 } (id's)
我希望在logcat中收到以下信息:
Log.d("TESTING", "forEach iteration before calling test(): 10")
Log.d("TESTING", "suspend test before receiving response: 10")
Log.d("TESTING", "response: 10")
Log.d("TESTING", "suspend test after receiving response: 10")
Log.d("TESTING", "forEach iteration after calling test(): 10")
___________________
Log.d("TESTING", "forEach iteration before calling test(): 15")
Log.d("TESTING", "suspend test before receiving response: 15")
Log.d("TESTING", "response: 15")
Log.d("TESTING", "suspend test after receiving response: 15")
Log.d("TESTING", "forEach iteration after calling test(): 15")
...但是,现在我得到的是这样的东西:
Log.d("TESTING", "forEach iteration before calling test(): 10")
Log.d("TESTING", "suspend test before receiving response: 10")
Log.d("TESTING", "suspend test after receiving response: 10")
Log.d("TESTING", "forEach iteration after calling test(): 10")
Log.d("TESTING", "forEach iteration before calling test(): 15")
Log.d("TESTING", "suspend test before receiving response: 15")
Log.d("TESTING", "suspend test after receiving response: 15")
Log.d("TESTING", "forEach iteration after calling test(): 15")
Log.d("TESTING", "response: 10")
Log.d("TESTING", "response: 15")
...基本上,每个循环都会移动,而不会等待API异步响应。我以为.await()是为了这个场景而设计的,但我可能在这里遗漏了一些东西,因为它显然不像我想象的那样起作用。
发布于 2020-02-24 13:19:53
变化
val apiRepsonse = async {
repository.doingApiCall() {
Log.d("TESTING", "response: ${item.id}")
}
}.await()至
val apiRepsonse = async {
Log.d("TESTING", "Start work in another 'thread'.")
repository.doingApiCall() {
Log.d("TESTING", "Finish work in another 'thread'.")
}
}.await()
Log.d("TESTING", "response: ${apiRepsonse.id}")这样,您等待传递的价值就被实际使用了。
我想这能消除你对LogCat的困惑。
我试着自己解决你的问题,我已经把它写成了一个可以运行的测试,可以理解:
class TestFoo {
@Test
fun foo() {
val job = GlobalScope.launch {
listOf("10", "15").forEach { item ->
println("TESTING forEach iteration before calling test(): ${item}")
test(item)
println("TESTING forEach iteration after calling test(): ${item}")
}
}
while(job.isActive) {
// wait
}
}
private suspend fun test(item: String) = coroutineScope {
println("TESTING suspend test before receiving response: ${item}")
val apiRepsonse = async {
delay(1000)
println("TESTING response: ${item}")
"Hello"
}.await()
println("TESTING suspend test after receiving response: ${item}")
}
}下面是"LogCat":

您是否因为repository.doingApiCall()中正在发生的线程处理而获得不同的日志?
发布于 2020-02-24 17:28:07
最有可能的是,repository.doingApiCall()不是一个适当的挂起函数,而是在不挂起协同线的情况下触发一些异步操作。看起来它需要一个lambda作为回调,这是另一个提示,即它不是一个挂起函数。
如果它是一个挂起函数,则不需要使用async来分派它。
顺便说一句,使用async并立即在其上调用await是使用withContext(Dispatchers.Default)的一种更复杂、更不优化的方法,它还可以默默地吞噬异常。
发布于 2020-02-24 13:41:10
没有测试,这应该有效:
// same code here
viewModelScope.launch {
list.forEach { item ->
Log.d("TESTING", "forEach iteration before calling test(): ${item.id}")
test()
Log.d("TESTING", "forEach iteration after calling test(): ${item.id}")
}
}
suspend fun test(item.id: String) = withContext(Dispatchers.IO) {
Log.d("TESTING", "suspend test before receiving response: ${item.id}")
repository.doingApiCall() {
Log.d("TESTING", "response: ${item.id}")
}
Log.d("TESTING", "suspend test after receiving response: ${item.id}")
}有两件事改变了:
不要启动新的async.await,而是使用withContext()来挂起withContext()中的调用者
withContext()https://stackoverflow.com/questions/60376171
复制相似问题