Синтаксис Kotlin async / await без блокировки вызывающей стороны - PullRequest
0 голосов
/ 02 ноября 2018

Я хотел бы выяснить, сможет ли Kotlin заменить наш текущий способ работы с асинхронным кодом. Прямо сейчас мы используем CompletableFutures для обработки асинхронного кода. Вот пример такого метода:

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

Важным моментом здесь является то, что onBalanceRequest вызывается из основного потока, который не должен быть заблокирован. Внутренне db.fetchBalance выполняет асинхронные операции и разрешает будущее по завершении, поэтому данный вызов не блокирует основной поток.

После просмотра документации Kotlin по сопрограммам я надеялся, что мы сможем сделать что-то вроде асинхронного / ожидающего выполнения JavaScript. Например, вот что мы можем сделать в 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 блокирует основной поток. Так что я спрашиваю вас, смогу ли я добиться чего-то похожего на async / await с Kotlin.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 02 ноября 2018

Конечно, 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()
}
0 голосов
/ 02 ноября 2018

Если ваша функция JS async, соответствующая функция Котлина должна быть suspend:

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

Нет необходимости в await, потому что Kotlin статически типизирован, и компилятор уже знает, какие функции suspend и должны быть обработаны специально (хотя C #, который также статически типизирован, использует async / await модель для явности).

Обратите внимание, что он может быть вызван только непосредственно из suspend функций; если вы хотите «запустить и забыть», используйте launch:

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

А для использования CompletableFuture функций возврата используйте kotlinx-coroutines-jdk8 :

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

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