在 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.ContinueWith
或 Promise.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");
}
这只有在您将挂起函数更改为非挂起阻塞函数时才有效,例如
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
库。
我不知道适用于多个平台的解决方案。
我正在学习 Kotlin 中的并发,来自 C#/JavaScript 背景,我忍不住要比较一些概念。
在 C# 和 JavaScript 中,从技术上讲,我们可以使用 Task.ContinueWith
或 Promise.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");
}
这只有在您将挂起函数更改为非挂起阻塞函数时才有效,例如
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
库。
我不知道适用于多个平台的解决方案。