Как вызвать функцию приостановки из другой функции приостановки, не блокируя вызывающую функцию?

#kotlin #asynchronous #kotlin-coroutines #suspend

#kotlin #асинхронный #kotlin-сопрограммы #приостановить

Вопрос:

Я вызываю Api из функции приостановки, и после ее успешного выполнения мне нужно вызвать другой Api (который также находится в другой функции приостановки).

     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)
        }
 

Я хочу вызвать updateSubscriptionApi и после его успешного выполнения вызвать fetchSubscription без блокировки и вернуть updateSubscription результат.

На данный момент fetchSubscription также блокируется updateSubscription . Я пытался вызвать updateSubscription такой async блок, но безуспешно.

async{ updateSubscription() }

Как я могу вызвать fetchSubscriptions() без блокировки updateSubscription .

Ответ №1:

К сожалению, ваше требование, чтобы функция одновременно приостанавливала и запускала параллельную работу, которая все еще активна при возврате функции, считается антишаблоном в Kotlin и имеет некоторые трудности в его достижении.

Но сначала простые вещи:

  1. Вы уверены, что вам нужен IO диспетчер? Вы сказали, что сетевой вызов приостанавливается, что означает неблокирующий и не требует выделенного диспетчера ввода-вывода.
  2. Если вам это нужно (на самом деле это блокирующий вызов), не включайте в него весь код, а только этот вызов.

Теперь самое сложное. Чтобы иметь возможность запускать сопрограмму, важно учитывать, в какой области вы ее запускаете. Kotlin настоятельно рекомендует использовать структурированный параллелизм, что означает запуск всего в четко определенной области действия вашего элемента пользовательского интерфейса (activity и т. Д.). В вашем случае вы должны явно передать область видимости в качестве параметра функции. Обычно вы объявляете ее как расширение on CoroutineScope , но это не работает для приостановки функций из-за коллизии имен on coroutineContext , которая является как глобальной val , так и свойством CoroutineScope . Это означает, что ваш код может выглядеть следующим образом:

 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())
}
 

Комментарии:

1. Да, мне нужен IO диспетчер, потому что мой вызов блокировался. Я попробовал ваше решение, и оно работает для меня. Спасибо