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

FireStore Tasks.whenAllComplete结合协程同步执行问题求助

问题描述

1. 协程与Firebase Tasks同步执行异常

尝试用job.join、deferred.await以及Firebase的await实现代码同步执行,但日志始终显示job-end先输出,Tasks的addOnComplete回调后执行,未达到预期效果。

相关代码:

CoroutineScope(Dispatchers.Main).launch {

    val job = launch {

        Tasks.whenAllComplete(tasks)
            .addOnCompleteListener {

                Log.d("user-get", "on-0-> addOnComplete")

                tasks.forEach { task ->

                    val snapshot = task.result

                    Log.d("user-get", "on->"+ task.toString())

                    snapshot.documents.forEach {
                        docs.add(it)
                    }

                }

            }
    }

    job.join()

    Log.d("user-get", "job-end")
}

日志顺序:

D/user-get: job-end

D/user-get: on-0-> addOnComplete

D/user-get: on->com.google.android.gms.tasks.zzw@5c0519d

尝试deferred+await、Task.whenAllComplete(tasks).addOnCompleteListener { ... }.await等写法,日志顺序仍一致。

2. 确认Firebase与协程结合的写法是否正确

编写了checkNickName挂起函数,执行符合预期,但想确认该写法是否为Firebase与协程结合的正确方式:

override suspend fun checkNickName(nickName: String): Results<Int> {


    lateinit var result : Results<Int>

    fireStore.collection("database")
        .document("user")
        .get()
        .addOnCompleteListener { document ->
            if (document.isSuccessful) {

                val list = document.result.data?.get("nickNameList") as List<String>

                if (list.contains(nickName))
                    result = Results.Exist(1)
                else
                    result = Results.No(0)

                //document.getResult().get("nickNameList")
            }
            else {

            }
        }.await()

    return result
}
解决方案

1. 修复协程与Tasks同步执行问题

当前代码中,job内部仅完成了回调注册操作,Tasks.whenAllComplete(tasks).addOnCompleteListener(...)会立即执行完毕,异步任务仍在后台运行,因此job快速结束,job.join()提前放行导致job-end先输出。

正确写法是去掉addOnCompleteListener,直接用Firebase Task的await()方法挂起协程,直到任务完成:

CoroutineScope(Dispatchers.Main).launch {
    val job = launch {
        // await()会挂起协程,直到所有任务完成并返回结果
        val allTasksResult = Tasks.whenAllComplete(tasks).await()
        
        Log.d("user-get", "on-0-> addOnComplete")
        allTasksResult.forEach { task ->
            val snapshot = task.result
            Log.d("user-get", "on->"+ task.toString())
            snapshot.documents.forEach {
                docs.add(it)
            }
        }
    }

    job.join()
    Log.d("user-get", "job-end")
}

调整后日志顺序会变为:

D/user-get: on-0-> addOnComplete
D/user-get: on->com.google.android.gms.tasks.zzw@5c0519d
D/user-get: job-end

2. 优化checkNickName函数的写法

原函数虽能运行,但存在两处隐患:

  • lateinit var result未处理任务失败场景,会导致未初始化就返回的异常;
  • else分支未处理任务失败的情况(如网络错误、权限问题)。

规范写法是直接在await()后处理结果,无需addOnCompleteListener,同时完善错误处理:

override suspend fun checkNickName(nickName: String): Results<Int> {
    return try {
        val document = fireStore.collection("database")
            .document("user")
            .get()
            .await() // 协程挂起直到任务完成

        if (document.isSuccessful) {
            val list = document.result.data?.get("nickNameList") as? List<String> ?: emptyList()
            if (list.contains(nickName)) {
                Results.Exist(1)
            } else {
                Results.No(0)
            }
        } else {
            // 返回任务失败对应的结果
            Results.Error(-1)
        }
    } catch (e: Exception) {
        // 捕获网络异常等情况
        Results.Error(-2)
    }
}

该写法既利用了协程的挂起特性,又规避了lateinit的风险,是Firebase与协程结合的正确方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:20:30