CompletableDeferred 使用和注意事项
结论
是的,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
的设计初衷就是为了手动控制完成时机:
-
桥接回调API:等待某个回调被触发
-
跨协程协调:一个协程等待另一个协程的信号
-
外部事件驱动:等待用户操作、网络响应等外部事件
// 典型用法:等待按钮点击
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}
}