结论

是的,CompletableDeferred 通常需要手动调用 complete() 或 completeExceptionally(),否则调用 await() 的协程会一直保持挂起状态。

CompletableDeferred 确实不适合数据流场景。它设计用于单次的异步结果,而不是持续的数据流。

为什么 CompletableDeferred 不适合数据流

关键区别

async 启动的 Deferred - 自动完成

val deferred = async {
    delay(1000)
    "自动返回的结果"  // 这个结果会自动设置给 deferred
}
// 不需要手动 complete,async 块执行完毕会自动完成

CompletableDeferred - 需要手动完成

val deferred = CompletableDeferred<String>()
// 此时 deferred 是"空"的,没有结果

// 如果不调用 complete(),await() 会永远挂起
// deferred.complete("结果")  // 如果注释掉这行...

val result = deferred.await()  // 这里会永远挂起!

为什么会这样设计?

CompletableDeferred 的设计初衷就是为了手动控制完成时机

  1. 桥接回调API:等待某个回调被触发

  2. 跨协程协调:一个协程等待另一个协程的信号

  3. 外部事件驱动:等待用户操作、网络响应等外部事件

// 典型用法:等待按钮点击
val buttonClickDeferred = CompletableDeferred<Unit>()

button.setOnClickListener {
    buttonClickDeferred.complete(Unit)  // 手动完成!
}

// 在协程中等待点击
suspend fun waitForButtonClick() {
    buttonClickDeferred.await()  // 挂起直到有人点击按钮
    println("按钮被点击了!")
}

重要提醒

如果您创建了 CompletableDeferred 但忘记调用 complete()

  • await() 会永远挂起

  • 协程永远不会恢复

  • 可能导致内存泄漏(挂起的协程无法被回收)

  • 程序可能看似卡死

最佳实践

// 总是确保有完成路径
suspend fun fetchData(): String {
    val deferred = CompletableDeferred<String>()
    
    try {
        // 设置超时保护
        withTimeout(5000) {
            // 启动异步操作
            startAsyncOperation { result ->
                deferred.complete(result)  // 确保这里会被调用
            }
            return deferred.await()
        }
    } catch (e: TimeoutException) {
        deferred.completeExceptionally(e)  // 超时也要完成
        throw e
    }
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐