Kotlin协程中onAwait与await()的区别及Android实现方法
Kotlin协程中onAwait与await()的区别及Android场景下的正确用法
核心区别
await():这是一个挂起函数,调用后会直接暂停当前协程,直到对应的Deferred任务执行完成,返回任务的计算结果。如果任务执行时抛出异常,await()会重新抛出该异常,需要用try-catch处理。onAwait:这是Deferred的一个属性,返回Job对象。它的作用是监听Deferred的完成事件,但不会暂停当前协程。你可以通过给它添加invokeOnCompletion回调,在任务完成(成功或失败)时触发逻辑,但它本身不返回任务结果,也不会阻塞协程流程。
你的代码问题分析
你写的代码里,async块内调用了viewModel.email.collect——如果email是StateFlow或SharedFlow,collect会一直挂起直到流被取消,而这类流通常不会主动结束。这就导致这个Deferred永远不会完成:
- 调用
resultEmail.await()会让当前协程永久挂起,永远拿不到结果; resultEmail.onAwait的回调也永远不会触发。
Android中的正确实现方式
场景1:持续收集流的更新
如果需要监听email的所有更新,直接在协程里调用collect即可,不需要用async:
var value = "" viewModelScope.launch { viewModel.email.collect { email -> value = email // 这里处理email的每次更新,比如更新UI } }
场景2:获取流的单次最新值
如果只需要获取一次email的最新值,用first()(挂起函数,获取第一个发射的值)或者直接访问StateFlow的value属性(非挂起,仅适用于StateFlow):
var value = "" viewModelScope.launch { // 方式1:挂起直到拿到第一个值(适用于所有Flow) val email = viewModel.email.first() value = email // 方式2:直接取StateFlow的最新值(非挂起,仅StateFlow可用) // value = viewModel.email.value }
场景3:确实需要用Deferred的并行任务
如果是并行执行多个耗时任务(比如同时请求两个接口),才需要用到async+await/onAwait:
viewModelScope.launch { // 启动并行任务 val emailDeferred = async { api.fetchUserEmail() } val nameDeferred = async { api.fetchUserName() } // 用await()等待结果,拿到后继续逻辑 val email = emailDeferred.await() val name = nameDeferred.await() // 处理拿到的email和name // 用onAwait监听任务完成事件,不阻塞当前协程 emailDeferred.onAwait.invokeOnCompletion { throwable -> if (throwable == null) { Log.d("TAG", "Email请求成功:${emailDeferred.getCompleted()}") } else { Log.e("TAG", "Email请求失败", throwable) } } }
正确使用场景总结
- 当你需要等待任务结果,并且要基于结果继续后续逻辑时,用
await(); - 当你不需要等待结果,只是想在任务完成时执行一些收尾逻辑(比如日志、状态上报),且不想阻塞当前协程时,用
onAwait配合invokeOnCompletion。
内容的提问来源于stack exchange,提问作者Riezki Maisyar
相关产品推荐
相关产品推荐

