You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Kotlin中的runBlocking正确实现阻塞效果?

Firestore异步操作导致runBlocking未阻塞的解决办法

问题现象

调用getGroupSummaries时,日志输出顺序不符合预期:

After loadUserInformationFromFirestore in runBlocking
After loadUserInformationFromFirestore after runBlocking
In LoadUserInformationFromFirestore

可见runBlocking没有等到loadUserInformationFromFirestore执行完成就继续执行后续代码,完全没起到阻塞作用。

问题原因

虽然loadUserInformationFromFirestore被声明为suspend函数,但内部使用了Firestore的addOnCompleteListener回调式API——这种异步操作不会挂起协程,函数会直接执行完毕返回,Firestore的查询结果要等回调触发才会处理,导致协程任务提前结束,runBlocking自然也跟着提前退出。

解决方案

要让协程真正等待Firestore操作完成,需将Firestore的回调式API转换为挂起函数,使用kotlinx-coroutines-play-services库提供的await()扩展函数即可实现(该库专门为Firebase的Task API提供协程支持)。

修改步骤

  1. 确保项目依赖中添加kotlinx-coroutines-play-services库
  2. 替换addOnCompleteListener为await(),让协程等待查询完成
  3. 简化协程调用逻辑(无需额外用viewModelScope.launch再join)

修改后的代码示例

var mFirestore: FirebaseFirestore? = null

fun getGroupSummaries(groupInfo: GroupInformation, adminUserInfo: UserInformation) {
    mFirestore = FirebaseFirestore.getInstance()

    val uid = "3iHWCsGmKvfq4w9OgDNWZsSs8wy2"

    runBlocking {
        loadUserInformationFromFirestore(uid)
        Timber.d("After loadUserInformationFromFirestore in runBlocking")
    }
    Timber.d("After loadUserInformationFromFirestore after runBlocking")
}

suspend fun loadUserInformationFromFirestore(uid: String) {
    try {
        val docRef = mFirestore!!.collection("Users").document(uid)
            .collection("UserProfile").document(uid)
        
        // 用await()替代回调,让协程等待Firestore查询完成
        val document = docRef.get().await()
        
        if (document.exists()) {
            Timber.d("In LoadUserInformationFromFirestore")
        } else {
            Timber.e("In LoadInformationFromFirestore, User document does not exist. Send user through into questions.")
        }
    } catch (e: Exception) {
        Timber.e("In LoadInformationFromFirestore, get Firestore failed with ", e)
    }
}

效果说明

修改后日志顺序会符合预期:

In LoadUserInformationFromFirestore
After loadUserInformationFromFirestore in runBlocking
After loadUserInformationFromFirestore after runBlocking

  • await()会自动处理Task的成功/失败状态,成功返回结果,失败抛出异常,可直接用try-catch统一捕获错误
  • runBlocking作用域内直接调用挂起函数即可等待操作完成,无需额外启动子协程再join

内容的提问来源于stack exchange,提问作者scrayne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:33:20