CompletableDeferred 使用和注意事项
CompletableDeferred需要手动调用complete()或completeExceptionally(),否则await()会永久挂起。与async自动完成的Deferred不同,CompletableDeferred专为手动控制完成时机设计,常用于桥接回调API、协程间协调或等待外部事件。若忘记调用complete()会导致协程挂起、内存泄漏等问题。最佳实践是确保设置完成路径,并建
·
结论
是的,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
}
}
更多推荐



所有评论(0)