如何通过 kotlin 协程避免调用 FireStore (Firebase) 的 api 中的深度嵌套回调

how to avoid deeply nested callbacks in call FireStore (Firebase)'s api by kotlin coroutine

如何在使用 Firebase/Firestore API 时防止深度嵌套回调?

Py 应用程序逐步调用 firestore api,我需要在 onSuccessonFailed.

时做一些处理

例如需要5步ps。为了进行下一步,需要参考预调用api的结果。

第一步:getA() // get A Data from firestore

第二步:if(resultGetA.isSuccess) getB() else updateC()

第三步:if(resultGetB.isSuccess) addD()

第四步:if(resultAddD.isSuccess) updateE()

第 5 步:if(resultUpdateE.isSuccess) getF()


kotlin 代码示例

这只是解释我的问题的示例来源, 但我的应用程序代码类似于这样:(

fun callHellExample1(email:String, pass:String, model:UserDataModel) {
        val collectRef = Firebase.firestore.collection("A")
        val auth = Firebase.auth

        auth.createUserWithEmailAndPassword(email, pass).addOnCompleteListener { createTask ->
            if (createTask.isSuccessful) {
                auth.signInWithEmailAndPassword(email, pass).addOnCompleteListener { signInTask ->
                    if (signInTask.isSuccessful) {
                        collectRef.add(model).addOnCompleteListener {
                            Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
                        }
                    } else {
                        Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
                    }
                }
            } else {
                Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
            }
        }
    }


fun callHellExample2(callback : (Boolean)-> Unit) {
        val collectRef = Firebase.firestore.collection("A")
        val auth = Firebase.auth

        collectRef.document("A").get().addOnCompleteListener { resultA ->
            if(resultA.isSuccessful){
                collectRef.document("B").get().addOnCompleteListener { resultB ->
                    if(resultB.isSuccessful){
                        collectRef.add("D").addOnCompleteListener { resultD ->
                            if(resultD.isSuccessful){
                                collectRef.document("E").update("someFiled", "someValue").addOnCompleteListener { resultE ->
                                    if(resultE.isSuccessful){
                                        collectRef.document("F").get().addOnCompleteListener {
                                            auth.signOut()
                                            callback(true)
                                        }
                                    }
                                }
                            }
                        }
                    }else{
                        Toast.makeText(this, "getB ... isSuccessful? = ${resultB.isSuccessful}", Toast.LENGTH_SHORT).show()
                    }
                }
            }else{
                collectRef.document("C").update("someFiled", "someValue").addOnCompleteListener { resultC ->
                    Toast.makeText(this, "update C ... isSuccessful? = ${resultC.isSuccessful}", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

所以我尝试使用协程。但我找不到关于 firestore 的逃生回调地狱 api

我试过这样(示例 1)。但它类似于回调地狱。 我想检查它是否成功。

await() return 不是 Task<AuthResult>。它只是 return AuthResult 但是 AuthResult 不包含 isSuccessful 变量

fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
        CoroutineScope(Dispatchers.IO).launch {
            try{
                auth.createUserWithEmailAndPassword(email, pass).await()
                try{
                    auth.signInWithEmailAndPassword(email, pass).await()
                    try{
                        collectRef.add(model).await()
                        withContext(Dispatchers.Main){
                            Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
                        }
                    }catch (e: Exception){
                        Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
                    }
                }catch (e: Exception){
                    Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
                }
            }catch (e: Exception){
                Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
            }
        }
    }

示例 2 无法显示吐司,因为也无法检查 isSuccessful。 不是 return 任务。它只是 return DocumentSnapShot

期待您的回复。 谢谢!

ps) 如果可以访问isSuccessful,代码可以这样编辑

fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
        CoroutineScope(Dispatchers.IO).launch {
            if(!auth.createUserWithEmailAndPassword(email, pass).await().isSuccessful){
                Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
                return@launch
            }

            if(!auth.signInWithEmailAndPassword(email, pass).await().isSuccessful){
                Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
                return@launch
            }
            
            if(collectRef.add(model).await().isSuccessful){
                Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
                return@launch
            }
                    
            withContext(Dispatchers.Main){
                Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
            }
        }
    }

我不会建议 re-implement 将 Task.await() 方法再次 return 任务本身,相反,您可以向标准 kotlin Result 添加一个简单的包装器 class:

import com.google.android.gms.tasks.Task
import kotlinx.coroutines.tasks.await

suspend fun <T> Task<T>.awaitResult() = runCatching { await() }

然后像这样使用它:

suspend fun foo(email: String, pass: String){
    if(auth.createUserWithEmailAndPassword(email, pass).awaitResult().isFailure){
        Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
        return
    }

    if(auth.signInWithEmailAndPassword(email, pass).awaitResult().isFailure){
        Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
        return
    }
}