首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将coroutine函数挂起,直到收到结果,然后继续

将coroutine函数挂起,直到收到结果,然后继续
EN

Stack Overflow用户
提问于 2020-02-24 12:40:25
回答 3查看 848关注 0票数 1

我正在慢慢地学习kotlin-coroutines,我想从简单的情况开始,在这种情况下,我遍历列表,每次调用一个API调用,等待响应,处理它,然后每次迭代都重复这个过程。

代码语言:javascript
复制
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中收到以下信息:

代码语言:javascript
复制
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")
...

但是,现在我得到的是这样的东西:

代码语言:javascript
复制
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()是为了这个场景而设计的,但我可能在这里遗漏了一些东西,因为它显然不像我想象的那样起作用。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-02-24 13:19:53

变化

代码语言:javascript
复制
val apiRepsonse = async {
        repository.doingApiCall() {
            Log.d("TESTING", "response: ${item.id}")
        }
    }.await()

代码语言:javascript
复制
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的困惑。

我试着自己解决你的问题,我已经把它写成了一个可以运行的测试,可以理解:

代码语言:javascript
复制
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()中正在发生的线程处理而获得不同的日志?

票数 2
EN

Stack Overflow用户

发布于 2020-02-24 17:28:07

最有可能的是,repository.doingApiCall()不是一个适当的挂起函数,而是在不挂起协同线的情况下触发一些异步操作。看起来它需要一个lambda作为回调,这是另一个提示,即它不是一个挂起函数。

如果它是一个挂起函数,则不需要使用async来分派它。

顺便说一句,使用async并立即在其上调用await是使用withContext(Dispatchers.Default)的一种更复杂、更不优化的方法,它还可以默默地吞噬异常。

票数 2
EN

Stack Overflow用户

发布于 2020-02-24 13:41:10

没有测试,这应该有效:

代码语言:javascript
复制
// 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()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60376171

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档