Is This a Good Way to Display Asynchronous Data

Is this a good way to display asynchronous data?

I would suggest using a DispatchGroup rather than keeping count of the number of items in 2 arrays. This would also allow to keep track of the progress across multiple threads with each block registered with enter() and leave() on the group. Then once all blocks have entered and left the notify block is called which could refresh your UI. Also using a dictionary with a place holder image in case the loading of one of the images fails. would be better than ignoring the failure case.

This may also be easier to read and reason since there is no tracking the count of 2 arrays. The intention is clearer with enter() and leave()

var imgs = [String: UIImage]()
var dispatchGroup = DispatchGroup()

override func viewDidAppear(_ animated: Bool) {

let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")

userRef.observeSingleEvent(of: .value, with: { [weak self] snapshot in // fetch list of objects from database
if (snapshot.value as? Bool) != nil { // User has no pets added
self?.loadScrollView() // Reload view
} else if let snap = snapshot.value as? NSDictionary { // User has pets
for value in snap {
self?.dispatchGroup.enter()
let imgRef = FIRStorage.storage().reference().child("profile_images").child(self!.pets[i]+".png")
imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
if let error = error {
print(error) // handle the error here but still call .leave() on the group to be sure the notify block is called.
imgs[value] = somePlaceHolderImage
self?.dispatchGroup.leave()
} else if let data = data, let image = UIImage(data: data) {
imgs[value] = image
self?.dispatchGroup.leave()
}

dispatchGroup.notify(queue: DispatchQueue.main, execute: {
self?.loadScrollView()
})
}
}
}
})
}

How to read asynchronous data from real-time database using android Kotlin?

The top answer to this related question shows you how to make callbacks, but that doesn't really answer the question of how to use the async data, and isn't really helpful or relevant to this type of problem.

I don't see anything specifically wrong with your callback - but it silently swallows a number of possible error cases (e.g. if the user doesn't exist). The example below has some print statements that should help determine better what is happening.

A cleaner approach than the extra callback interface is to make a separate method to handle the async result. Here is a cleaned up example of how that might look - with some pseudo-code where parts of your example were missing. To help debug, you should get in the habit of using log or print statements if you don't understand what parts of the code are running, or if something doesn't look the way you expect it to.

private var currentImageUrl: String = ""
private var userId: String = ""
private lateinit var su_name: TextView
private lateinit var su_image : ImageView
private lateinit var su_layout : ConstraintLayout

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)

// get views
su_name = findViewById<TextView>(R.id.su_name)
su_image = findViewById<ImageView>(R.id.su_image)
su_layout = findViewById<ConstraintLayout>(R.id.su_layout)

su_layout.visibility = View.INVISIBLE

// get user id from intent
userId = intent.getStringExtra("su_userid").orEmpty()

// TODO: Handle what to do if userId is empty here!
if( userId.isEmpty() ) {
finish()
}

// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }

// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
startLoading()
}

private fun startLoading() {
println("LOG: getting data for ${userId}")

val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(userId)

suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()) {
println("LOG: data snapshot exists")
val userData = dataSnapshot.getValue(profile_model::class.java)
showData(userData)
}
else {
println("LOG: user not found in database")
}
}

override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}

private fun showData(userData: profile_model?) {
su_layout.visibility = View.VISIBLE

currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: "Error"

println("LOG: Got user data ${currentImageUrl}")

if( currentImageUrl.isNotEmpty() ) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
}

private fun viewCurrentImage() {
if( currentImageUrl.isEmpty() ) return

val imageViewer = Intent(this, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}

Angular 2 - Displaying async Object data from promise

You do not need any special pipe. Angular 2 suppport optional field. You just need to add ? in your object

{{ (data | async)?.name }}

or

{{(name | async)?}}


Related Topics



Leave a reply



Submit