【问题标题】:why my coroutine async only works inside a runBlocking?为什么我的协程异步只能在 runBlocking 内工作?
【发布时间】:2020-02-06 14:08:25
【问题描述】:

我正在尝试理解协程,但似乎比预期的更难理解,也许有人可以给我正确的方法。

我想要一个调用挂起函数的端点(简单的 hello world)。

为此我做了这个:

@GET
@Path("/test")
suspend fun test() : String {
    coroutineScope {
        async {
            doSomething()
        }.await()
    }
    return "Hello"
}

在 doSomething() 中我很简单地这样做了

private fun doSomething(){
   logger.info("request")
}

看起来很简单直接,阅读 async https://kotlinlang.org/docs/reference/coroutines/composing-suspending-functions.html 它需要一个协程范围 https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html ,所以我的代码应该可以工作。

但是当我调用我的方法时,我得到了这个:

! kotlin.KotlinNullPointerException: null
! at kotlin.coroutines.jvm.internal.ContinuationImpl.getContext(ContinuationImpl.kt:105)
! at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:179)

关于此的 NPE

 public override val context: CoroutineContext
        get() = _context!!

coroutineScope 移动为runBlocking 时,它可以工作。知道我缺少什么吗?我怎样才能使这项工作? (我尽量避免使用GlobalScope.async

我正在使用 dropwizard 作为框架

【问题讨论】:

    标签: java kotlin kotlin-coroutines


    【解决方案1】:

    不要让您的控制器功能成为挂起功能。它们只能从其他挂起函数或协程中调用。

    我不确切知道您的端点是如何正确构建的,但由于它已执行 - 内部没有协程上下文 - 因为我们没有定义任何!这就是为什么您会为上下文获得 NPE。

    顺便说一句:下面的代码不会有异步行为,因为您会立即等待 - 就像正常的顺序代码一样:

    async {
        doSomething()
    }.await()
    

    为了快速解决你的问题,这里我将如何重写它:

    @GET
    @Path("/test")
    fun test() : String {
        GlobalScope.launch { // Starts "fire-and-forget" coroutine
           doSomething() // It will execute this in separate coroutine 
        }
        return "Hello" // will be returned almost immediately 
    }
    

    要了解有关上下文的更多信息,请阅读thisTLDR:使用 Kotlin 的构建器和函数来创建上下文 - 例如 runBlocking


    编辑

    为了避免GlobalScope.函数,我们可以使用runBlocking

    @GET
    @Path("/test")
    fun test() : String = runBlocking {
        val deferredResult1 = async { doSomething() } // Starts immediately in separate coroutine
        val deferredResult2 = async { doSomethingElse() } // Starts immediately in separate coroutine
    
        logger.print("We got:${deferredResult1 .await()} and ${deferredResult2 .await()}")
    
        "Hello" // return value - when both async coroutines finished
    }
    

    【讨论】:

    • 感谢您的回复,有什么办法可以避免使用 GlobalScope?
    • 添加了另一个解决方案 - 稍微扩展以使案例现实
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    • 1970-01-01
    • 2019-02-19
    相关资源
    最近更新 更多