Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
311 views
in Technique[技术] by (71.8m points)

android - Realtime Database how to wait for asynchronous task in iteration?

What I'm doing here is getting users groups as hashmap, then getting each group's info and doing other stuff with it. But at the end of the foreach loop my "groups" list is empty. Is there anything I can do to wait for foreach loop to finish? Thank you.

db
    .child("users")
    .child(firebaseUser.uid)
    .get()
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
        val document = task.result

        if (document != null && document.value != null) {
            val groupsObject =
                document.value["groups"] as HashMap<String, Boolean>

            for (groupID in groupsObject.keys) {
                db
                    .child("groups/$groupID")
                    .addValueEventListener(object : ValueEventListener {
                        override fun onDataChange(snapshot: DataSnapshot) {
                            db
                                .child("users")
                                .child(friendID)
                                .get()
                                .addOnCompleteListener { task ->
                                    // check for something
                                    // add to groups list if data is OK
                                }
                        }

                        override fun onCancelled(error: DatabaseError) {
                            // TODO: ...
                        }
                    })
            }

            // here "groups" data is empty, because that for each loop above
            // hasn't finshed yet looping, and thus my list is displayed empty
            groupsAdapter = GroupsAdapter(this@GroupsActivity, groups)
            recyclerView.adapter = groupsAdapter
        }
    }
}
question from:https://stackoverflow.com/questions/65872140/realtime-database-how-to-wait-for-asynchronous-task-in-iteration

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There is no way to wait for all data to be loaded without blocking the user from using your app. That's why the rule of thumb is always the same: any code that needs the data from the database needs to be inside the addOnCompleteListener callback, or be called from there.


The idiomatic way is to not wait for the data to be loaded, but instead tell the adapter about the new data as it comes in. You can do that by calling notifyDataSetChanged inside your addOnCompleteListener:

db
    .child("groups/$groupID")
    .addValueEventListener(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            db
                .child("users")
                .child(friendID)
                .get()
                .addOnCompleteListener { task ->
                    // check for something
                    // add to groups list if data is OK
                    groupsAdapter.notifyDataSetChanged()
                }
        }

In cases where you really must run some code after all data has loaded, a common approach is to keep a counter of the number of friends you've already loaded, and check against that inside the addOnCompleteListener.

So something like:

.addOnCompleteListener { task ->
    // check for something
    // add to groups list if data is OK
    
    if (friendsLoadedCount++ === groupsObject.keys.size()) {
        // TODO: all friends have loaded, so do something with them
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...