3

I am calling an Api from a suspend function and after successful execution of it, I need to call another Api (which is also in another suspend function).

    suspend fun updateSubscription(body: Map<String, Any>): NetworkResponse<SubscriptionUpdateResponse> =
        withContext(Dispatchers.IO) {
            val response = networkManager.execute(
                networkManager.updateSubscriptionApi(body)
            )
            val data = response.body()
            if (response.isSuccessful) {
                fetchSubscriptions() // suspend function which call another api, should run without blocking
            }
            return@withContext parseNetworkResponse(response, data)
        }

I want to call updateSubscriptionApi and after it successful execution, call fetchSubscription without blocking and return updateSubscription result.

For now, fetchSubscription is also blocking updateSubscription. I tried to call updateSubscription in async block like this without any success.

async{ updateSubscription() }

How can I call fetchSubscriptions() without blocking updateSubscription.

ahsanali274
  • 471
  • 2
  • 14

1 Answers1

1

Unfortunately, your requirement for the function to be both suspending and launching concurrent work that's still active when the function returns, is considered an anti-pattern in Kotlin and has some difficulties in achieving it.

But first, the simple things:

  1. Are you sure you need the IO dispatcher? You said the network call is suspending, which means non-blocking and doesn't require a dedicated IO dispatcher.

  2. If you do need it (it's actually a blocking call), don't wrap all the code in it, but just that call.

Now, the hard part. To be able to launch a coroutine as well, it's critical to consider which scope you're launching it in. Kotlin strongly recommends to use structured concurrency, which means launching everything in a well-defined scope of your UI element (activity etc.). In your case you must pass the scope explicitly as a parameter to the function. Normally you'd declare it as an extension on CoroutineScope, but that doesn't work for suspending functions due to the naming collision on coroutineContext, which is both a global val and the property of CoroutineScope. That means your code could look as follows:

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

suspend fun updateSubscription(
        scope: CoroutineScope,
        body: Map<String, Any>
): NetworkResponse<SubscriptionUpdateResponse> {
    val response = withContext(Dispatchers.IO) { // only if you need it!
        networkManager.execute(networkManager.updateSubscriptionApi(body))
    }
    if (response.isSuccessful) {
        scope.launch { fetchSubscriptions() }
    }
    return parseNetworkResponse(response, response.body())
}
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Yes I needed `IO` dispatcher because my call was blocking. I have tried you solution and it is working for me. thanks – ahsanali274 Nov 26 '20 at 10:25