【问题标题】:how to avoid deeply nested callbacks in call FireStore (Firebase)'s api by kotlin coroutine如何避免kotlin协程调用FireStore(Firebase)的api中深度嵌套的回调
【发布时间】:2022-09-28 19:35:58
【问题描述】:

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

Py app 一步一步调用firestore api,当onSuccessonFailed 时我需要做一些处理。

例如,它需要 5 个步骤。为了进行下一步,它需要参考 pre-call api 的结果。

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

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

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

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

第五步: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() 返回不是Task<AuthResult>。它只是返回AuthResultAuthResult 不包含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 无法显示 toast 因为也无法检查 isSuccessful。 不是返回任务。它只是返回 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()
            }
        }
    }
  • 这段代码中究竟有什么不能按您期望的方式工作?告诉我们共享代码有什么问题。你有什么错误吗?

标签: android firebase kotlin google-cloud-firestore kotlin-coroutines


【解决方案1】:

我不建议重新实现 Task.await() 方法以再次返回任务本身,而是可以向标准 kotlin Result 类添加一个简单的包装器:

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
    }
}

【讨论】:

    【解决方案2】:

    协程与 Google Play Services Tasks API 集成。

    只需添加此依赖项:

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.1'
    

    您可以使用Task.asDeferred 将任务转换为延迟任务。 同样,Task.await 等待任务完成(可取消)。

    你可以在这里阅读更多信息:https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-06
      • 2013-12-01
      • 2011-06-21
      • 2019-05-18
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多