【问题标题】:Kotlin: Make the function wait to return until for loop is complete [duplicate]Kotlin:使函数等待返回直到for循环完成[重复]
【发布时间】:2021-06-04 05:08:52
【问题描述】:

我有一个从 Firebase Firestore 检索值的简单函数。我想返回值。如何让函数等到检索到值,而不是立即返回空值?

请仅使用 Kotlin。

         fun fireStoreGetter(keyVal: String): String {
            Log.d("FIRESTORE_OP", "fGet running with keyVal: " + keyVal)
            //logs: FIRESTORE_OP: fGet running with keyVal: exampleString
            var userId :String = ""
            var mFirebaseDatabaseInstances = FirebaseFirestore.getInstance()
            val user = FirebaseAuth.getInstance().currentUser
            if (user != null) {
                userId = user.uid
                //Log.e(TAG, "User data is null")
            } else {
                Log.d("FIRESTORE_OP", "MUST AUTHENTICATE TO ACCESS FIRESTORE")
            }
            Log.d("FIRESTORE_OP", "USER IN WITH UID: " + userId)
            //logs FIRESTORE_OP: USER IN WITH UID: DGGiDibnldhP5z6iUyf_GQ
            val docRef2 = mFirebaseDatabaseInstances.collection("users").document(userId).collection("dBase").document("exampleDoc")
            docRef2.get()
            .addOnSuccessListener { document ->
                //for (document in result) {
                        if (document != null) {
                            //docVal is instantiated in main thread
                            docVal = document.getString(keyVal).toString()//String(keyVal)
                            Log.d("FIRESTORE_OP", "DOCVAL SET: " + docVal)
                            //logs: FIRESTORE_OP: DOCVAL SET: (correct value from firestore)
                        }
            }
        Log.d("FIRESTORE_OP", "RETURNING fGet: " + "VALUE: " + docVal)
        //logs: FIRESTORE_OP: RETURNING fGet: VALUE:       
        return docVal
        //returns empty
     }

【问题讨论】:

  • 你需要使用回调接口。或者如果你使用 Kotlin 协程,你可以通过 suspendCoroutine 来完成。
  • @a_local_nobody,OP 明白为什么 value 是空的,他问如何等待他的监听器执行
  • @VictorCold 不记得说什么了,这就是为什么它是一个问答帖子来解释如何解决它
  • 您无法通过方法返回docVal。 Firebase API 是异步的。因此,请检查副本以了解如何使用自定义回调解决此问题。正如 Kamal Nayan 在他的评论中已经提到的那样,您也可以使用 Kotlin Coroutine 来实现这一点。

标签: android kotlin asynchronous google-cloud-firestore


【解决方案1】:

所以,这就是你现在尝试的方式:

fun myFunction() {
    val value = fireStoreGetter(keyVal)
    ...
}

fun fireStoreGetter(keyVal: String): String {
    val docRef2 = ...
    docRef2.get().addOnSuccessListener { document ->
        for (document in result) {
            if (document != null) {
                docVal = ...
            }
        }
    }
    return docVal
}

你应该这样做:

fun myFunction(value: String) {
    ...
}

fun fireStoreGetter(keyVal: String) {
    val docRef2 = ...
    docRef2.get().addOnSuccessListener { document ->
        for (document in result) {
            if (document != null) {
                docVal = ...
            }
        }
        myFunction(docVal)
    }
}

您的 docRef2.get() 返回一个 Task 实例。它没有等待其结果的功能,例如在挂起协程函数或类似的东西中。所以唯一的方法是不要从fireStoreGetter函数返回结果,而是在结果准备好时触发myFunction

UPD:原来有一种方法可以等待 Task 的结果。如果您确实需要摆脱回调,请参阅Kamal's 答案。但请记住,这将需要一些额外的调整。协程、挂起函数等

【讨论】:

  • 谢谢你。这是一个很好的解决方法。希望我能在某个时候从 Kamal 那里得到上述实现。在那之前我会用这个。
【解决方案2】:

您需要添加以下依赖项才能使用await()

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

并在函数名称中使用suspend关键字,并在firebase的成功或失败监听器后添加.await()。这将使代码等待函数从 firebase 检索数据。

You can read this blog for reference

你的代码应该是这样的:

  suspend fun fireStoreGetter(keyVal: String): String {
        Log.d("FIRESTORE_OP", "fGet running with keyVal: " + keyVal)
        //logs: FIRESTORE_OP: fGet running with keyVal: exampleString
        var userId :String = ""
        var mFirebaseDatabaseInstances = FirebaseFirestore.getInstance()
        val user = FirebaseAuth.getInstance().currentUser
        if (user != null) {
            userId = user.uid
            //Log.e(TAG, "User data is null")
        } else {
            Log.d("FIRESTORE_OP", "MUST AUTHENTICATE TO ACCESS FIRESTORE")
        }
        Log.d("FIRESTORE_OP", "USER IN WITH UID: " + userId)
        //logs FIRESTORE_OP: USER IN WITH UID: DGGiDibnldhP5z6iUyf_GQ
        val docRef2 = mFirebaseDatabaseInstances.collection("users").document(userId).collection("dBase").document("exampleDoc")
        docRef2.get()
        .addOnSuccessListener { document ->
            //for (document in result) {
                    if (document != null) {
                        //docVal is instantiated in main thread
                        docVal = document.getString(keyVal).toString()
                    
                    }
        }
     return docVal
       }.await()

【讨论】:

  • 你能详细说明一下实际的实现吗?我在尝试实现这一点时遇到了很多问题。
  • 我已经更新了答案
  • 得到关于最后一行的“期望成员声明”错误 --> }.await()
  • 我尝试了几种不同的方法。仍然收到“期望成员声明”错误。在这样的函数声明之后,我从未使用过 .await() 。我在 build.gradle 中添加了实现 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1' 并在脚本中导入 kotlinx.coroutines.tasks.await。还是一样的问题。
  • 请分享日志。
猜你喜欢
  • 1970-01-01
  • 2012-06-02
  • 2022-01-22
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
  • 2014-07-19
  • 2019-05-11
相关资源
最近更新 更多