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()
}
}
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.
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.
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.
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:
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.
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!"
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With