为什么我的协程异步只能在runBlocking内部工作?

时间:2019-10-09 09:10:43

标签: java kotlin kotlin-coroutines

我试图理解协程,而且似乎比预期的要难理解,也许有人可以为我提供正确的方法。

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

为此,我做到了:

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

在doSomething(),我简单地做到了

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

看起来非常简单和直观,阅读有关异步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作为框架

1 个答案:

答案 0 :(得分:0)

不要使控制器功能成为挂起功能。只能从其他暂停函数或协程调用它们。

我不知道您的端点是如何构建的,没有错误,但是由于它是执行的-内部没有协程上下文-因为我们没有定义任何关联!这就是为什么您需要上下文的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 
}

要了解有关上下文的更多信息,请阅读this TLDR :使用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
}