Kotlin异步/等待语法,不会阻塞调用方

时间:2018-11-02 11:44:53

标签: kotlin kotlinx.coroutines

我想弄清楚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的功能,请问您。

谢谢。

2 个答案:

答案 0 :(得分:4)

如果您的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#(也是静态类型的)使用{{ 1}} / async模型的明确性。

请注意,只能直接从await函数中调用它;如果您想“抛弃”它,请使用suspend

launch

要使用private fun onBalanceRequest(client: Client) = GlobalScope.launch { val money = db.fetchBalance(client.name) client.sendMessage("Your money: $money") } 返回函数,请使用kotlinx-coroutines-jdk8

CompletableFuture

答案 1 :(得分: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()
}