Aync with await

from the CommonsWare Community archives

At December 12, 2020, 2:09am, Jan asked:

    in viewModel I have results when last line here is reached whether I use deferred/await or not:
    viewModelScope.launch(Dispatchers.Main) {
        val deferred = viewModelScope.async(Dispatchers.IO) {
            val result = APIManager.getData()

        } // deferred
     val result2 = deferred.await()
     >>>last line here   --->>>> I have my results from the call to getData available

     //Regular version:
    viewModelScope.launch(Dispatchers.Main) {
            val result = APIManager.getData()

     >>>last line here  --->>>> I have my results from the call to getData available without using await

     QUESTION: why don't I need the deferred.await? by the time last line here is reached, I have the results of APIManager.getData()
     BUT what I really want is the activity to wait for the results so that those results can be put on the screen.  
     But a deferred.await in the activity has NO results to process.
     I tried it with viewmodel.viewModelScope and with GlobalScope and the activity is not "awaiting" the call to finish. Does await work differently in an Activity?
     Activity calls version 1:
             viewModel.viewModelScope.launch(Dispatchers.Main) {
             val deferred1 = viewModel.viewModelScope.async(Dispatchers.Default) {
                val result2 = viewModel.loadMyData()
             val result = deferred1.await()   ----> this line is not waiting for return of loadMyData!!!

      Activity calls version 2 attempt with different scope:

           GlobalScope.launch(Dispatchers.Main) {
            val deferred2 = GlobalScope.async(Dispatchers.IO) {
                val result2b = viewModel.loadMyData()

            val result4 = deferred2.await()   ----> this line is not waiting for return of loadMyData!!!

At December 12, 2020, 12:51pm, mmurphy replied:

async() and await() are replacements for launch(), for cases where you want to start a coroutine and have code outside the coroutine get a result. In your case, you are using launch() in both places.

BUT what I really want is the activity to wait for the results so that those results can be put on the screen.

Have your ViewModel expose a LiveData, StateFlow, or perhaps SharedFlow, and have the activity observe that.

At December 12, 2020, 1:01pm, Jan replied:

Okay. I thought you needed the launch from your book’s example:
fun main() {
GlobalScope.launch(Dispatchers.Main) {
val deferred = GlobalScope.async(Dispatchers.Default) {
println(“This is executed after the delay”)

I’m trying to put the result list in an adapter. So in my observe, would I make the adapter call:
myRecycler.adapter = MyRecyclerAdapter( some list returned from the network call)

My boss isn’t fond of using viewModels and wants everything in a controller which is one reason I was trying to avoid observes - plus it seemed weird to finish off the adapter there.

At December 12, 2020, 1:27pm, mmurphy replied:

Sorry, my previous reply was muddled (it is too early in the morning here to be thinking about coroutines!).

The reason for the outer launch() is because await() itself is a suspend function, so you need that to be in a coroutine. Basically, everything gets rooted in a launch().

I do not know what APIManager.getData() does and whether it is a suspend function or not. Similarly, I do not know what loadMyData() does and whether it is a suspend function or not.

Personally, outside of the examples that you see in the Klassbook, I do not think that I have used async()/await() much. Partly, that is because I have been stuck doing more work in RxJava, as that is what my customers have been using. Partly, that is because the particular patterns that I tend to use have not needed async()/await().

About the only scenario off the top of my head where I would lean towards async()/await() is where I had several bits of work that could go on in parallel, but then I would need to wait for all the results before proceeding — the same scenario where in RxJava you might use zip(). In this case, you would use async() to set up each piece of work and use awaitAll(job1, job2, job3) to await all of them to complete (or for one to get cancelled).

At December 12, 2020, 3:02pm, Jan replied:

So I’m trying to use LiveData and observe. Is there a way that you know to convert the list I get back to LiveData? If I use a repository, I can do it but that’s not what I have.

At December 12, 2020, 3:17pm, mmurphy replied:

Sorry, but I do not know what “the list I get back” means.

In my books, I have some examples of using LiveData with lists. For example, the Weather sample gets a list of weather forecasts, and the Bookmarker sample gets a list of bookmarks. The ToDo app from Exploring Android gets lists of to-do items.

At December 12, 2020, 3:34pm, Jan replied:

Okay. I thought I could slip this in with an hour’s of work but obviously not. Just thought there was an easy way to go from List to LiveData<List>. But we are talking Android and nothing is easy. :slight_smile: Thanks for your help.

At December 12, 2020, 3:49pm, mmurphy replied:

It’s more that I don’t really understand the question. Literally, what you are asking for is:

val wtf = MutableLiveData<List<String>>().apply { value = yourList }

(pretending that this is a list of strings and that yourList is the list that you want to “convert”, and I’m doing this by eyeball in a forum post editor so there might be a typo in there)

However, if you already have the list, probably you do not need the LiveData. This becomes more useful if you need to do work to build the list, and you want that to happen on a background thread. The various examples that I linked to show that. In those cases, I am using Room and Retrofit, which provide their own dispatchers, so you don’t necessarily need to specify Dispatchers.IO.

At December 12, 2020, 5:33pm, Jan replied:

Thanks. It looks like that value thing was the trick.