Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Coroutines how to wait for the data and then continue process

I'm learning coroutines with kotlin, and I have problem how process can wait until process 1 finished then it continue to process 2, from my sample below I have object Network which access API server using getNews(it's running well and get the data) I called this getNews from refreshNews using asynch - await, with the purpose it wait for the data then it continue running, but "The program Not Wait", it just running process 2 then process 1 finish, so I cannot capture data from API in refresh news

// process 1 - calling api this running well can get the data see process 2

object Network {
    var status : NewsApiStatus =  NewsApiStatus.LOADING

    private var viewModelJob = Job()
    private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)

    fun getNews(filter: String, page: Int =1) : newsData? {
        var allNews : newsData? = null
        coroutineScope.launch {
            RetrofitClient.instance.getAllNews(filter, page).enqueue(object: Callback<newsData>{
                override fun onFailure(call: Call<newsData>, t: Throwable) {
                    status = NewsApiStatus.ERROR
                }

                override fun onResponse(
                    call: Call<newsData>,
                    response: Response<newsData>
                ) {
                    status = NewsApiStatus.DONE
                    var listResult = response.body()
                    if (listResult != null) {
                        if (listResult.data.isNotEmpty())  {
                            allNews = listResult
                            Timber.tag(TAG).i( "process 1 total allNews = ${allNews!!.data.size}")
                        }
                    }
                }
            })
        }
        return(allNews)
    }
}

// process 2 - calling process 1 with runBlocking

fun refreshNews() = runBlocking{
    val newsData = async {
        Network.getNews("")
    }
    Timber.tag(TAG).i("proses 2 ${newsData.await()?.data?.size}")
    // here I want newsData to wait until it has data
}

// this main program that call process 2

class NewsListViewModel(application: Application) : AndroidViewModel(application) {
    init {
        refreshNews()
    }
}
like image 555
widjajayd Avatar asked Jun 03 '20 08:06

widjajayd


People also ask

How do you wait for coroutine to complete?

We can wait for the coroutine to finish by calling join() on the Job. For example, suppose we have a suspend function to download some files. We can launch this coroutine and capture the resulting job, which we can later use to join — to wait for the operation to complete.

How does coroutine delay work?

Launched coroutines each wait for a delay for 2 seconds. Running this gives this result. All items are processed with just a little over 2 seconds despite each coroutine delaying by 2 seconds. To make things interesting, runBlocking is not associated with a dispatcher backed by a thread pool.

Can coroutines be suspended and resumed?

If you notice the functions closely, they can be used to resume the coroutine with a return value or with an exception if an error had occurred while the function was suspended. This way, a function could be started, paused, and resume with the help of Continuation. We just have to use the suspend keyword.

How to wait for a job to complete in a coroutine?

In cases where we don’t need the return value of the coroutine, we have the option to use the launch function. The launch function is an extension of CoroutineScope that returns a Job. We call the Job#join method to wait for the Job to complete. Additionally, if we have a collection of Jobs, then we call joinAll to wait until all of them complete:

How to cancel a coroutine without waiting it to finish?

It can be seen that the Log Statement is not allowed to execute until that coroutine which is running finishes its work and it happens possibly only due to the join () method. cancel () method is used to cancel the coroutine, without waiting for it to finish its work.

How to wait for a coroutine to finish in a thread?

Use the Job.join (): Unit method to wait for a coroutine to finish before continuing with current the thread: //launch coroutine var result = "" val request = launch { delay (500) result = "Hello, world!" } //join coroutine with current thread request.join () //print "Hello, world!"

How to control a coroutine in Java?

AbstractCoroutine implements the Job interface. So a coroutine is a job. You can control a coroutine through the functions available on the Job interface. Here are some of the functions (there are many more): Further Job operations will be explored in future posts. To wait for a coroutine to finish, you can call Job.join.


Video Answer


1 Answers

launch returns a reference to the started job. You can use it to wait for the job to finish by calling join():

val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job
    // ...
}
runBlocking {
    job.join() // wait until child coroutine completes
}

Currently, your getNews() launches a coroutine and immediately returns. allNews isn't initialised at that point yet.

You need to either call job.join() inside getNews() (would make it blocking), or use async inside getNews() and return its result if you want to keep it asynchronous (you'd need to take the result differently from your http client as you won't be able to initialise the variable declared outside).

It's worth to go through the official coroutine docs:

  • https://kotlinlang.org/docs/reference/coroutines/basics.html
  • https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html
like image 95
Jakub Zalas Avatar answered Oct 27 '22 03:10

Jakub Zalas