skip to Main Content

I want to implement this code synchronously
but job.join, deferred.await, and firebase await, not working.
Does anyone know a solution?

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

Log order:

D/user-get: job-end

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

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

deferred and await, and Task.whenAllComplete(tasks).addOnCompleteListener { ~~~ }.await
are also same log result.

really appreciate Alex! the below is the code that I mentioned at comment

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
}

Is it proper way to use both firebase and coroutine?
it was executed as I expected

2

Answers


  1. I think what you need to do is to not use addOnCompleteListener and use await() function on the Task object instead:

    coroutineScope.launch {
    
        val tasks = Tasks.whenAllComplete(tasks).await() // Awaits the completion of the task without blocking a thread.
        
        tasks.forEach { task ->
    
            val snapshot = task.result
    
            Log.d("user-get", "on->"+ task.toString())
    
            snapshot.documents.forEach {
                docs.add(it)
            }
        }
    
        Log.d("user-get", "job-end")
    
    }
    
    Login or Signup to reply.
  2. As I commented in an earlier question of yours, there is no need to launch a coroutine when you attach a listener. It’s one, or the other. When you call Tasks#whenAllComplete(Task...<?> tasks), the type of object that is returned is Task<List<Task<?>>. This means that all the heavy work is done behind the scenes, in a different thread. So there is nothing that can block the main thread. When you attach a listener, it means that you’ll wait until the operation is complete, or fails with an exception. So you either get the request out of the coroutine or, as @Sergio mentioned in his answer, remove the listener and call await(). Please also note that this function is a suspend function and not a blocking function.

    Now, since this operation can raise an exception, is recommended to add the following lines of code inside a method, perhaps in a repository class, and use a try-catch together with Kotlin flow:

    fun getDocs() = flow {
        try {
            val tasks = Tasks.whenAllComplete(tasks).await()
            tasks.forEach { task ->
                val snapshot = task.result
                snapshot.documents.forEach {
                    docs.add(it)
                }
            }
            emit(docs)
        } catch (e: Exception) {
            emit(e)
        }
    }
    

    And inside a ViewModel class launch a coroutine and collect the result:

    fun getDocs() = viewModelScope.launch {
        repo.getDocs().collect { docs ->
            //Do what you need to do with the documents
        }
    }
    

    So most likely you might consider converting the list of DocumentSnapshot objects into a list of custom objects of yours. That being said, an even simpler solution, would be not to iterate but to call QuerySnapshot#toObjects(Class clazz):

    val tasks = Tasks.whenAllComplete(tasks).await()
    val docs = tasks.toObjects(YourModelClass::class.java)
    

    I have written an article called:

    Where I explain step by step how you can achieve that. Here is the corresponding repo. And here is a line of code where I do the the conversion.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search