Как мне назначить переменную с `val` внутри блока кода, чтобы она была доступна впоследствии?

Я хочу сделать что-то вроде следующего:

suspend fun getProducts(): List<Product> {
  runBlocking {
    launch {
      val inAppProducts = withContext(Dispatchers.IO) {
        queryProductDetails(...)
      }
    }
    launch {
      val subsProducts = withContext(Dispatchers.IO) {
        queryProductDetails(...)
      }
    }
  }
  return inAppProducts + subsProducts
}

(Примечание: для краткости игнорирование нулевых проверок и аргументов функций)

Два вопроса:

  1. Как мне инициализировать val и использовать его вне блока кода? var единственный вариант?
  2. Это хороший стиль кодирования?

Вы можете просто удалить runBlocking, что бесполезно.

dominicoder 12.04.2023 02:31

@dominicoder Плохо - обновил мой вопрос, чтобы включить launch.

Plasty Grove 12.04.2023 02:34
0
2
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я собираюсь предположить, что причина для runBlocking и запуска двух сопрограмм заключается в том, что вы хотите, чтобы они выполнялись параллельно, а затем ждали обоих результатов. Если да, то для этого и нужен async.

Пример псевдокода, который может быть или не быть технически правильным, но вы иллюстрируете идею:

suspend fun getProducts(): List<Product> {
    // Start first job and get a handle to it
    val inAppProductsJob = async {
      withContext(Dispatchers.IO) {
        queryProductDetails(...)
      }
    }

    // Start second job and get a handle to it
    val subsProductsJob = async {
      withContext(Dispatchers.IO) {
        queryProductDetails(...)
      }
    }

  // Waits for both jobs to complete, gets their results, and combines them
  return inAppProducts.await() + subsProducts.await()
}

Вам нужно обернуть эту функцию либо в withContext, либо в coroutineScope, иначе у вас не будет доступа к приемнику CoroutineScope для вызова async.

Tenfour04 12.04.2023 06:23

Да, я просто подправил собственный код OP, чтобы проиллюстрировать идею, я не проверял его на правильность.

dominicoder 12.04.2023 06:26

Я предполагаю, что они использовали runBlocking, чтобы иметь возможность запускать другие сопрограммы, поэтому отсутствие знаний о withContext и coroutineScope, которые дают вам область действия сопрограммы без блокировки, — половина их проблемы.

Tenfour04 12.04.2023 06:29

Вместо async { withContext(Dispatchers.IO) { ... } } почему бы и нет async(Dispatchers.IO) { ... }?

Louis Wasserman 12.04.2023 14:12
Ответ принят как подходящий

Никогда не используйте runBlocking в функции приостановки. Функция приостановки не должна блокироваться по соглашению. Вместо этого вы можете использовать coroutineScope или withContext, чтобы иметь возможность запускать параллельные сопрограммы внутри вашей функции приостановки.

launch для сопрограмм, которые ничего не производят. Вместо этого используйте async, если вы хотите что-то произвести. Это сразу запускает сопрограмму параллельно. Затем вызовите await() в Deferred сопрограммы, чтобы получить результат в функции приостановки.

suspend fun getProducts(): List<Product> = coroutineScope {
  val inAppProducts = async {
    queryProductDetails(...)
  }
  val subsProducts = async {
    queryProductDetails(...)
  }
  return@withContext inAppProducts.await() + subsProducts.await()
}

Если queryProductDetails() — функция приостановки, то переключение на диспетчер ввода-вывода бессмысленно (если только мы не присоединяемся к огромным спискам), и мы можем заменить withContext на coroutineScope.

Спасибо, что поделился! Итак, queryProductDetails — это функция приостановки ссылка. Я не могу добавить async, если я не включу его внутрь withContext(Dispatchers.IO), то есть val inAppProducts = withContext(Dispatchers.IO) { async { queryProductDetails(...)}}. Это кажется правильным?

Plasty Grove 12.04.2023 08:54

Обратите внимание на крайний withContext в моем коде — его не нужно использовать дважды. Но поскольку queryProductDetails — это функция приостановки, вы можете использовать вместо нее coroutineScope, чтобы сделать ее немного проще. withContext и coroutineScope в основном одинаковы, за исключением того, что withContext также позволяет вам изменять CoroutineContext.

Tenfour04 12.04.2023 12:13

Ах, я пропустил самый крайний withContext, спасибо! Теперь это работает. Но вы правы в другом комментарии, там много о withContext и coroutineScope, чего я не совсем понимаю. Есть ли у вас какие-либо рекомендуемые ресурсы, которые помогут лучше понять их?

Plasty Grove 12.04.2023 12:25

Я не знаю ни одного ресурса, который четко объясняет все это в одном месте. :( Документация для функции coroutineScope довольно ясна и лаконична. Вы можете думать о withContext как об одном и том же плюс возможность изменять CoroutineContext. Чаще всего он используется для принудительного применения определенного Dispatcher. Вам нужно только диспетчер при вызове кода блокировки или при вызове функции, которая работает только в определенном потоке, например при работе с представлениями, которым требуется Main.

Tenfour04 12.04.2023 14:49

Другие вопросы по теме