Home > front end >  Why do these two methods shows different results?
Why do these two methods shows different results?

Time:09-18

I was trying to fetch an arraylist from Firestore using coroutine kotlin. I wrote two different methods to get same result. These are the methods

MethodA

fun methodA(type: String) {
           CoroutineScope(IO).launch {
             val myList = mutableListOf<Product>()
             val job = async(IO) {
              db.collection("Products").get().addOnSuccessListener { snapShot ->
                 snapShot.forEach { subSnapShot ->
                    if (subSnapShot.exists()) {
                        if (subSnapShot["name"]?.equals(type) == true) {
                            val category = type
                            val url = subSnapShot["url"].toString()
                            val type = subSnapShot["type"].toString()
                            val item = Product(url, category, subSnapShot.id, type)
                            myList.add(item)
                        }
                    }
                }
              }
            myList
         }.await()
          temp.postValue(job)
       }
      }

MethodB

 fun methodB(type: String) {
    CoroutineScope(IO).launch {
              val myList = mutableListOf<Product>()
               db.collection("Products").get().addOnSuccessListener { snapShot ->
                snapShot.forEach { subSnapShot ->
                  if (subSnapShot.exists()) {
                      if (subSnapShot["name"]?.equals(type) == true) {
                          val category = type
                          val url = subSnapShot["url"].toString()
                          val type = subSnapShot["type"].toString()
                          val item = Product(url, category, subSnapShot.id, type)
                          myList.add(item)
                      }
                  }
              }
          }.await()
        temp.postValue(myList)
    }
}

I was expecting to get same result from these two methods but MethodA returns an empty list while MethodB returns list of item. How do i fix my MethodA if i fetch list like this unlike MethodB

CodePudding user response:

Neither of these will reliably work. It's a race condition, and you got lucky with MethodB. It won't consistently be successful.

The problem is that even though you are calling await() on the task, the callback will not necessarily be run before await() returns.

A listener is called asynchronously. Code below it continues running in the same thread it was added from. Then the code inside the listener is run some time in the future (probably only a couple milliseconds, but still after your other code is run) when the result is complete.

Since you're using coroutines, you should be using the return value of await() and not be adding listeners. You also need to use try/catch in case it fails. Then your code can be run sequentially in the order it appears, taking advantage of one of the best features of coroutines.

In practice, you should be using an existing CoroutineScope that you cancel at the appropriate time, not creating a one-off CoroutineScope to launch a single coroutine and then throwing it away. But I'm leaving that in this example since it's not what you were asking about.

Here's how to properly do this (except for the CoroutineScope part):

fun methodC(type: String) {
    CoroutineScope(IO).launch {
        try { 
            db.collection("Products").get().await()
                .filter { it.exists() && it["name"]?.equals(type) == true }
                .map { subSnapShot ->
                    val category = type
                    val url = subSnapShot["url"].toString()
                    val type = subSnapShot["type"].toString()
                    Product(url, category, subSnapShot.id, type)
                }
                .let(temp::postValue)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to query products.", e)
        }
    }
}
  • Related