Kotlin coroutineScope supposed to call all async functions concurrently - kotlin

I am new to Kotlin and coroutines.
I have the below code:
fun asyncCallForRelationIdList(relationIds: List<String>): List<Any?> = runBlocking {
println("Starting asynchronous code flow")
asyncCallForUserIdList(relationIds)
}
suspend fun asyncCallForUserIdList(userIds: List<String>): List<Any?> = coroutineScope {
println("Elements in list are supposed to call api concurrently")
val listval = mutableListOf<Any?>()
val result0 = async {restApiCall(relationId = userIds[0])}
val result1 = async {restApiCall(relationId = userIds[1])}
listval.add(result0.await())
listval.add(result1.await())
listval.toList()
}
suspend fun restApiCall(relationId: String): Any? {
println("api call... the api will timeout with an artificial delay")
//api call
//return "api result"
}
asyncCallForRelationIdList() function will call asyncCallForUserIdList() which then makes an API call that will timeout.
My question is about the async keyword that I am using in coroutineScope. I expect the 2 async calls to happen concurrently. But that is not the case here. After the first api call, the thread waits for the service to timeout instead of making the 2nd api call concurrently. Why?

you are using runBlocking so its will block the main thread and run one by one. and its not Starting asynchronous code flow.

Change your async calls to async(Dispatchers.IO) { ... } because they're not actually asynchronous, currently they're running in the scope of runBlocking
Thanks to #Pawel

Related

Kotlin async function not run in parallel

I have a kotlin function just as following, And I expected it can wrap a sync IO action into async.
suspend fun <T> runIOAsync(f:suspend () -> T): Deferred<T> = coroutineScope{
async(Dispatchers.IO) {
f()
}
}
Then I have my code in the calling side like
runBlocking {
repeat(5) {
runIOAsync {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}.await()
}
}
But the actual out put is
0
Thread[DefaultDispatcher-worker-1 #coroutine#2,5,main]
After sleep 0
1
Thread[DefaultDispatcher-worker-1 #coroutine#3,5,main]
After sleep 1
2
Thread[DefaultDispatcher-worker-1 #coroutine#4,5,main]
After sleep 2
3
Thread[DefaultDispatcher-worker-1 #coroutine#5,5,main]
After sleep 3
4
Thread[DefaultDispatcher-worker-1 #coroutine#6,5,main]
After sleep 4
Which seems all tasks from my function are executed serially. Any one can please help to give an explanation
Let's put aside runIOAsync for the moment.
You're using await() right after calling async(), which means that you're effectively waiting for the end of your async execution before executing the next one.
Instead, start all tasks first and then await all of them. For instance you can use awaitAll:
runBlocking {
List(5) {
async(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}.awaitAll()
}
Also, the way you're encapsulating the scope in runIOAsync is wrong, you will be waiting for the end of the async execution even without calling await() (coroutineScope is a suspending function that waits for all its child coroutines before resuming).
Instead, use coroutineScope to define the boundary of your coroutines executions, and you don't even have to await them. Since you don't need to get values from this code, you can also use launch instead of async here:
coroutineScope {
repeat(5) {
launch(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}
}
Declaring a suspend function returning a Deferred should be a red flag, and is quite confusing from an API standpoint: if you suspend it means you wait, if you return Deferred it means you don't wait (you immediately return a handle to some running computation). A function that does both would be quite weird.
If what you want is to make suspending code from IO-bound code, you can use instead the existing withContext function:
// this suspends until the code inside is done
withContext(Dispatchers.IO) {
// run some blocking IO code
}
However, note that this is independent from defining concurrent code. If you want to run multiple things concurrently, even with suspend functions, you'll need coroutine builders such as async or launch like in the above code.

Mix and match Coroutines and Rxjava

Coroutines and RxJava3
I have the following method that first makes a call to a suspend method and in the same launch scope I make 2 calls to RxJava.
I am wondering if there is a way to remove the Rxjava code out of the viewModelScope.launch scope and return the result of fetchRecentUseCase.execute().
Basically, is it possible for the viewModelScope.launch to return the listOfProducts rather than doing everything in the launch scope?
fun loadRecentlyViewed() {
viewModelScope.launch {
val listOfProducts = withContext(Dispatchers.IO) {
fetchRecentUseCase.execute()
}
val listOfSkus = listOfProducts.map { it.sku }
if (listOfSkus.isNotEmpty()) {
loadProductUseCase.execute(listOfSkus)
.subscribeOn(schedulersFacade.io)
.flatMap(convertProductDisplayUseCase::execute)
.map { /* work being done */ }
.observeOn(schedulersFacade.ui)
.subscribeBy(
onError = Timber::e,
onSuccess = { }
)
}
}
}
Usecase for the suspend method
class FetchRecentUseCaseImp() {
override suspend fun execute(): List<Products> {
// Call to network
}
}
Many thanks in advance
With coroutines, the way to return a single item that is produced asynchronously is to use a suspend function. So instead of launching a coroutine, you mark the function as suspend and convert blocking or async callback functions into non-blocking code.
The places where coroutines are launched are typically at UI interactions (click listeners), or when classes are first created (on Android, this is places like in a ViewModel constructor or Fragment's onViewCreated()).
As a side note, it is against convention for any suspend function to expect the caller to have to specify a dispatcher. It should internally delegate if it needs to, for example:
class FetchRecentUseCaseImp() {
override suspend fun execute(): List<Products> = withContext(Dispatchers.IO) {
// Synchronous call to network
}
}
But if you were using a library like Retrofit, you'd simply make your Request and await() it without specifying a dispatcher, because await() is a suspend function itself.
So your function should look something like:
suspend fun loadRecentlyViewed(): List<SomeProductType> {
val listOfSkus = fetchRecentUseCase.execute().map(Product::sku)
if (listOfSkus.isEmpty()) {
return emptyList()
}
return runCatching {
loadProductUseCase.execute(listOfSkus) // A Single, I'm assuming
.await() // Only if you're not completely stripping Rx from project
.map { convertProductDisplayUseCase.execute(it).await() } // Ditto for await()
.toList()
.flatten()
}.onFailure(Timber::e)
.getOrDefault(emptyList())
}

Can I get return actual value if I use launch in Kotlin?

I'm learning Coroutines of Kotlin.
The Code A use async in the coroutines of Kotlin and I can use .await() on a deferred value to get its eventual result, so one.await() will return Int.
If I use launch in the coroutines, can I get the actual value just like one.await()?
Code A
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
There is no [standard non-hacky] way to get the result from launch. This is the whole point of having async.
The 2 functions launch and async have precisely one difference, which is that launch is fire-and-forget, and async allows to wait for a result.
Therefore, there should be no reason for you to want to use launch over async unless you don't need the result, so the question is quite suprising.
If you use launch, the "actual value" is Unit as you can see from the signature
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job (source)
so you don't even need to start it.
If you pass a lambda to launch as in
launch { doSomethingUsefulOne() }
it is really
launch { doSomethingUsefulOne(); Unit }
and the value of doSomethingUsefulOne() is thrown away.
The short answer is NO
As you pointed out async and await will get you the result.
But Launch is used for a different purpose. Its purpose is to act as a bridge between Coroutine and NonCoroutine worlds.
Consider an example of a ViewModel whose coroutine world is controlled by viewModelScope
fun nonCoroutineWorldFunction() {
....
....
viewModelScope.launch {
// runs in coroutine world
}
....
....
viewModelScope.launch {
// runs in coroutine world
}
}
Launch can be considered something similar to FIRE AND FORGET. You just launch it to do its job rather than waiting for it to do its job

From Multiple Kotlin Coroutines suspended function to a single synchronous function

So, I have multiple suspended functions that looks like this
suspend fun getLatestCampaign(): List<Campaign> {
return listOf()
}
suspend fun getRecommendedCampaign(): List<Campaign> {
return listOf()
}
Since I want to run these function asynchronously, so I did it like this
val recommendedCampaignAsync = async(Dispatchers.IO) { getRecommendedCampaign() }
val latestCampaignAsync = async(Dispatchers.IO) { getLatestCampaign() }
And I also have function that will process the result of these async operations
fun displayCampaigns(campaigns: List<Campaign>) {}
What should I do, if I want every time one of these async operation complete, I want to call displayCampaigns and make sure displayCampaigns is executed synchronously.
So I don't want to wait both of these async operation to complete and then call displayCampaigns
async is not the tool for your job because you want to consume the result synchronously.
Dispatchers.IO is not the one you should use because you call suspendable functions (which I'll assume are non-blocking, otherwise they shouldn't be declared as suspendable).
What you want is the simplest case: launch two coroutines in the Main dispatcher, which should already have been set up as the default one:
class YourGuiClass : WhateverYourFrameworkWantsYouToExtend, CoroutineScope {
override val coroutineContext = Job() + Dispatchers.Main
fun refreshCampaigns() {
launch {
displayCampaigns(getRecommendedCampaign())
}
launch {
displayCampaigns(getLatestCampaign())
}
}
}
Either call displayCampaigns inside of the async blocks
val recommendedCampaignAsync = async(Dispatchers.IO) {
val campaigns = getRecommendedCampaign()
displayCampaigns(campaigns)
}
val latestCampaignAsync = async(Dispatchers.IO) {
val campaigns = getLatestCampaign()
displayCampaigns(campaigns)
}
or add a CompletionHandler to the jobs returned by async
recommendedCampaignAsync.invokeOnCompletion{
displayCampaigns(recommendedCampaignAsync.getCompleted())
}
latestCampaignAsync.invokeOnCompletion{
displayCampaigns(latestCampaignAsync.getCompleted())
}

MutableLiveData: Cannot invoke setValue on a background thread from Coroutine

I'm trying to trigger an update on LiveData from a coroutine:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.value = getAddressList()
}
return AddressList
}
but I get the following error:
IllegalStateException: Cannot invoke setValue on a background thread
Is there a way to make it work with coroutines?
Use liveData.postValue(value) instead of liveData.value = value. It called asynchronous.
From documentation:
postValue - Posts a task to a main thread to set the given value.
You can do one of the following :
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.postValue(getAddressList())
}
return AddressList
}
or
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
val adresses = getAddressList()
withContext(Dispatchers.Main) {
AddressList.value = adresses
}
}
return AddressList
}
I just figured out that it's possible by using withContext(Dispatchers.Main){}:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
GlobalScope.launch {
withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
}
return AddressList
}
Although others have pointed out that, in this case, the library provides its own method to post an operation to the main thread, coroutines provide a general solution that works regardless of a given library's functionality.
The first step is to stop using GlobalScope for background jobs, doing this will lead to leaks where your activity, or scheduled job, or whatever unit of work you invoke this from, may get destroyed, and yet your job will continue in the background and even submit its results to the main thread. Here's what the official documentation on GlobalScope states:
Application code usually should use application-defined CoroutineScope, using async or launch on the instance of GlobalScope is highly discouraged.
You should define your own coroutine scope and its coroutineContext property should contain Dispatchers.Main as the dispatcher. Furthermore, the whole pattern of launching jobs within a function call and returning LiveData (which is basically another kind of Future), isn't the most convenient way to use coroutines. Instead you should have
suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
and at the call site you should launch a coroutine, within which you can now freely call getAddresses() as if it was a blocking method and get the addresses directly as a return value.
If you want to updated UI by using Coroutines, there are 2 ways to achieve this
GlobalScope.launch(Dispatchers.Main):
GlobalScope.launch(Dispatchers.Main) {
delay(1000) // 1 sec delay
// call to UI thread
}
And if you want some work to be done in background but after that you want to update UI, this can be achieved by the following:
withContext(Dispatchers.Main)
GlobalScope.launch {
delay(1000) // 1 sec delay
// do some background task
withContext(Dispatchers.Main) {
// call to UI thread
}
}
In my case, I had to add Dispatchers.Main to the launch arguments and it worked fine:
val job = GlobalScope.launch(Dispatchers.Main) {
delay(1500)
search(query)
}
I was facing this error when I called a coroutine inside runBlockingTest for a test case
TestCoroutineRule().runBlockingTest {
}
I got it fixed by adding the instantExecutorRule as a class member
#get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
Below code worked for me for updating livedata from thread:
val adresses = getAddressList()
GlobalScope.launch {
messages.postValue(adresses)
}