В каких случаях использовать Coroutines не прагматично

Ответ

Coroutines не подходят для CPU-bound задач (тяжёлые вычисления), т.к. они выполняются в одном потоке и могут заблокировать диспетчер. В таких случаях лучше использовать RxJava или чистые потоки.

Также не стоит использовать корутины для:

  • Долгих синхронных операций (например, работа с файлами в основном потоке)
  • Когда нужен точный контроль над потоками (корутины абстрагируют управление потоками)
  • В простых асинхронных задачах, где ListenableFuture или колбэки достаточно

Пример CPU-bound задачи:

// Плохо - заблокирует Dispatchers.IO
suspend fun calculateFactorial(n: Int): BigInteger = withContext(Dispatchers.IO) {
    var result = BigInteger.ONE
    for (i in 1..n) result = result.multiply(BigInteger.valueOf(i.toLong()))
    result
}

Ответ 18+ 🔞

Ага, слушай, вот тут тема важная, как доширак в три часа ночи. Короче, корутины — это не универсальный ебаный отмычка на все случаи жизни. Нельзя ими всё подряд пихать, как мартышлюшка банан.

Вот смотри, для CPU-bound задач — это когда твоему процессору надо реально ебушки-воробушки устроить, считать что-то тяжёлое — они нихуя не годятся. Почему? Да потому что они, по сути, в одном потоке и вертятся. Начнёшь там факториал какого-нибудь космического числа считать в корутине, и всё, диспетчер заблокируешь нахуй. Весь твой асинхронный рай накроется медным тазом. Для такого дела бери RxJava или работай с чистыми потоками, там уж точно не промажешь.

И вообще, корутины — не панацея. Ещё их не стоит юзать, если:

  • У тебя долгая синхронная операция, типа файлы гигабайтные читаешь в основном потоке. Это пидарас шерстяной уровень архитектуры. Терпения ноль ебать у системы будет.
  • Когда тебе нужен тотальный контроль над потоками, как у сапёра над миной. Корутины же эту хуйню абстрагируют, ты не всегда поймёшь, где что выполняется. Доверия ебать ноль, если ты параноик.
  • Для простых асинхронных задач, где хватит старого доброго ListenableFuture или колбэков. Зачем городить огород из корутин, если можно просто? Да похуй, в общем.

Вот, смотри, пример, как делать НЕ НАДО. Прямо хитрая жопа получится:

// Плохо - заблокирует Dispatchers.IO
suspend fun calculateFactorial(n: Int): BigInteger = withContext(Dispatchers.IO) {
    var result = BigInteger.ONE
    for (i in 1..n) result = result.multiply(BigInteger.valueOf(i.toLong()))
    result
}

Видишь? Объявил функцию suspend, засунул в Dispatchers.IO, и думаешь, что красавчик. А на деле эта хуйня будет тупо цикл крутить в потоке из пула воркеров для IO, и если n будет овердохуища, то все остальные IO-операции встанут в очередь, как за халявой. Сам от себя охуел, когда это осознал. Для вычислений — отдельный диспетчер, отдельный поток, отдельная всё. Не смешивай мух с котлетами, а то будет тебе вилкой в глаз или в жопу раз.