在 Kotlin 中,是否可以在不破坏调用者的情况下用非暂停版本替换暂停乐趣?

In Kotlin, is it possible to substitute a suspend fun with a non-suspend version, without breaking the caller?

我正在学习 Kotlin 中的并发,来自 C#/JavaScript 背景,我忍不住要比较一些概念。

在 C# 和 JavaScript 中,从技术上讲,我们可以使用 Task.ContinueWithPromise.then 等将 async 函数重写为执行相同操作的常规非异步版本.

函数的调用者甚至不会注意到差异(我在 blog post 中大声疾呼)。

Kotlin 中的 suspend 函数是否可以实现类似的功能(即不更改调用代码)?我不认为是,但我想我还是要问一下。

我能想到的最接近的是下面的 (Kotlin playground link),我仍然需要调用 .await():

import kotlinx.coroutines.*

suspend fun suspendableDelay(ms: Long): Long {
    delay(ms);
    return ms;
}

fun regularDelay(ms: Long): Deferred<Long> {
    val d = CompletableDeferred<Long>()
    GlobalScope.async { delay(ms); d.complete(ms) }
    return d;
}

suspend fun test(ms: Long): Long {
    delay(ms);
    return ms;
}

fun main() {
    val r1 = runBlocking { suspendableDelay(250) }
    println("suspendableDelay ended: $r1");
    val r2 = runBlocking { regularDelay(500).await() }
    println("regularDelay ended: $r2");
}

https://pl.kotl.in/_AmzanwcB

这只有在您将挂起函数更改为非挂起阻塞函数时才有效,例如

private fun method(){
    GlobalScope.launch {
        val value = getInt()
    }
}

// Calling coroutine can be suspended and resumed when result is ready
private suspend fun getInt(): Int{
    delay(2000)  // or some suspending IO call
    return 5;
}

// Calling coroutine can't be suspended, it will have to wait (block)
private fun getInt(): Int{
    Thread.sleep(2000)   // some blocking IO 
    return 5;
}

这里可以简单的使用非挂起版本,对调用者没有任何改变。

但这里的问题是,如果没有 suspend 修饰符,函数就会变得阻塞,因此它不会导致 coroutine 挂起,基本上放弃了使用 coroutiens 的优势。

如果您使用的是 JVM 8 或更高版本,则可以创建一个调用异步作业中的挂起函数的函数和 returns CompletableFuture,它可用于通过回调获取结果 ( thenApplyAsync()) 或同步 (get()).

val scope = CoroutineScope(SupervisorJob())

suspend fun foo(): Int {
    delay(500)
    return Random.nextInt(10)
}

fun fooAsync(): CompletableFuture<Int> = scope.async { foo() }.asCompletableFuture()

fun main() {
    fooAsync()
        .thenApplyAsync { println(it) }
    Thread.sleep(1000)
}

以上需要 kotlinx-coroutines-jdk8 库。

我不知道适用于多个平台的解决方案。