Kotlin中能否不修改调用方代码将suspend函数替换为非挂起版本?
结论
你之前的判断正确,Kotlin无法在不修改调用代码的前提下,将suspend挂起函数替换为等价的非挂起版本。
与C#/JavaScript的差异原因
- C#中
async方法的返回值是Task/Task<T>,非async方法也可以手动构造并返回相同类型的Task,调用方无论你内部实现是否用了async语法,都可以统一用await或者ContinueWith处理,完全感知不到差异。 - JavaScript中
async函数的返回值是Promise,非async函数也可以手动返回Promise,调用方统一用await或者then处理,同样感知不到内部实现差异。 - Kotlin的
suspend是函数类型的固有属性,编译器层面强制要求挂起函数只能在协程作用域或者其他挂起函数中调用。如果要把返回T的挂起函数改成普通函数实现相同的异步逻辑,普通函数只能返回Deferred<T>类型,调用方必须额外调用.await()才能拿到T类型的返回值,不可能做到调用代码完全无修改。
最优近似实现方案
你给出的代码已经是目前能做到的最接近的实现,通过普通函数返回Deferred<T>来模拟挂起函数的异步逻辑,但确实需要调用方增加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") }
本质设计差异
Kotlin的挂起函数设计核心是把异步执行的语义暴露给编译器,实现编译期的协程转换,从语法层面就区分了同步普通函数和异步挂起函数,和C#/JavaScript把异步性全部封装在返回值类型里的设计有本质区别,所以不存在C#/JavaScript那种无缝替换的可能性。
内容的提问来源于stack exchange,提问作者noseratio
相关产品推荐
相关产品推荐

