【问题标题】:Kotlin async/await syntax without blocking callerKotlin async/await 语法不阻塞调用者
【发布时间】:2018-11-02 11:44:52
【问题描述】:

我想知道 Kotlin 是否可以取代我们当前处理异步代码的方式。现在,我们使用CompletableFutures 来处理异步代码。以下是此类方法的示例:

public void onBalanceRequest(Client client, String name) {
  db.fetchBalance(name)
    .thenAccept(balance -> {
       client.sendMessage("Your money: " + balance);
    });
}

这里的重点是onBalanceRequest 是从主线程调用的,不能被阻塞。在内部,db.fetchBalance 运行异步操作并在完成时解析未来,因此给定的调用不会阻塞主线程。

在查看了有关协程的 Kotlin 文档后,我希望我们可以做一些类似于 JavaScript 的 async/await 的事情。例如,这是我们在 JavaScript 中可以做的:

async function onBalanceRequest(client, name) {
  let balance = await db.fetchBalance(name);
  client.sendMessage("Your money: " + balance);
}

现在,我尝试将我们现有的 API 连接到 Kotlin 项目:

private fun onBalanceRequest(client: Client) = runBlocking {
    val money = db.fetchBalance(client.name)
    client.sendMessage("Your money: $money")
}

suspend fun fetchBalance(player: String): Double? {
    var result: Double? = null
    GlobalScope.launch {
        originalFetchBalance(player).thenAccept {
            result = it
        }
    }.join()
    return result
}

但是,由于我使用了runBlocking,所以onBalanceRequest 的执行阻塞了主线程。所以我问你,如果我能用 Kotlin 实现类似于 async/await 的东西。

谢谢。

【问题讨论】:

  • 如果你一开始不想屏蔽,为什么还要使用runBlocking
  • 因为我不知道我可以尝试什么。就是那个问题。这只是一个形象化“想法”的例子。
  • runBlocking 用于阻塞代码... launch 用于非阻塞 ;-)
  • runBlocking 替换为launch 时,我的代码将无法编译。我不太明白为什么,但我认为它不能在那里使用?
  • “不会编译”是什么意思?你在使用 Kotlin 1.3 吗?

标签: kotlin kotlinx.coroutines


【解决方案1】:

如果你的JS函数是async,那么对应的Kotlin函数应该是suspend

private suspend fun onBalanceRequest(client: Client) {
    val money = db.fetchBalance(client.name)
    client.sendMessage("Your money: $money")
}

不需要await,因为 Kotlin 是静态类型的,编译器已经知道哪些函数是 suspend 并且需要特殊处理(尽管 C# 也是静态类型的,使用 async/@987654328 @ 明确性模型)。

注意只能直接从suspend函数调用;如果您想“一劳永逸”,请使用launch

private fun onBalanceRequest(client: Client) = GlobalScope.launch {
    val money = db.fetchBalance(client.name)
    client.sendMessage("Your money: $money")
}

要使用您的CompletableFuture-returning 函数,请使用kotlinx-coroutines-jdk8

// should be suggested by IDE
import kotlinx.coroutines.future.await

suspend fun fetchBalance(player: String) = originalFetchBalance(player).await()

【讨论】:

  • 谢谢。 .await() 在这里不存在,但 .join() 确实并且似乎可以完成这项工作。
  • 我们可以更正/讨论这个问题,以便我可以将您的答案标记为解决方案吗?
  • 你添加了kotlinx-coroutines-jdk8的依赖吗?
  • 没有,但是您是否发现仅使用 .join() 有任何问题,因为它似乎无需添加额外的依赖项即可工作?
  • 它阻塞了它被调用的线程,用这段代码它不会是主线程。
【解决方案2】:

当然runBlocking 会阻塞运行。所以改为使用launch

private fun onBalanceRequest(client: Client) = GlobalScope.launch {
    val money = db.fetchBalance(client.name)
    client.sendMessage("Your money: $money")
}

要桥接您的CompletableFuture,您可以使用CompletableDeferred 然后

suspend fun fetchBalance(player: String): Double {
    val async = CompletableDeferred()
    originalFetchBalance(player).whenComplete { (value, error) ->
        if (value != null) async.complete(value)
        if (error != null) async.completeExceptionally(error)
    }
    return async.await()
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    相关资源
    最近更新 更多