Почему можно вызвать coroutine только из suspend функции

Ответ

Coroutines требуют специального контекста выполнения, который предоставляется CoroutineScope. Suspend-функции работают внутри этого контекста и могут приостанавливать выполнение без блокировки потока. Обычные функции не имеют доступа к этому механизму.

Пример:

suspend fun fetchData() { 
    // Можно вызывать корутины, т.к. это suspend-функция
    withContext(Dispatchers.IO) { /* ... */ }
}

fun regularFunction() {
    // Ошибка: нельзя вызвать корутину напрямую
    // withContext(Dispatchers.IO) { ... } 
}

Для вызова корутин из обычных функций используйте CoroutineScope.launch или runBlocking.

Ответ 18+ 🔞

Э, слушай, тут про корутины вопрос. Ну, типа, объясняю на пальцах, чтобы даже мартышлюшка поняла.

Представь себе, что корутина — это такой специальный работяга, который умеет делать вид, что работает, а сам в это время может чай попить. Но для этого ему нужна своя, блядь, мастерская — CoroutineScope. Без этой мастерской он нихуя не может, просто стоит как пень.

Suspend-функция — это как раз наш хитрый работяга. Он заходит в эту мастерскую (CoroutineScope) и говорит: «Окей, я тут постою, пока данные грузятся, а вы пока другим делом займитесь». И поток не блокируется! Обычная же функция — это как тупой робот: начал делать — и будет делать до конца, пока не упрётся лбом в стенку. Он не умеет приостанавливаться, поэтому и не может просто так взять и вызвать корутину. Ему для этого нужен надсмотрщик.

Вот смотри на код, тут всё ясно:

suspend fun fetchData() {
    // Тут всё ок, мы в suspend-функции, можно приостанавливаться
    withContext(Dispatchers.IO) { /* ... */ } // Сказал "погоди" и пошёл файлы качать
}

fun regularFunction() {
    // А тут будет пиздец и компилятор наорет: "Чего блядь?!" 
    // withContext(Dispatchers.IO) { ... } // Обычная функция не знает, как это "подождать"
}

Чувак, regularFunction просто охуеет от такой наглости. Она же не в курсе про всю эту магию приостановок! Ей кажется, что withContext должен выполниться прямо сейчас, а это невозможно.

Так как же быть, если очень надо? Да похуй, выход есть всегда. Если тебе из обычной, тупой как валенок, функции нужно запустить корутину, есть два классических способа:

  1. CoroutineScope.launch — это как нанять нового работягу прямо на месте. Сказал ему: «Слушай, вот тебе задача, делай», а сам пошёл дальше по коду. Асинхронно, красиво.
  2. runBlocking — это уже отчаяние, ёпта. Это когда ты говоришь: «Всё, стоп, ребята. Никто отсюда не уйдёт, пока этот долбоёб fetchData не выполнится». Поток заблокируется, и будет ждать. Используй это только в крайних случаях, а то получишь хиросиму и нигерсраки в виде тормозов.

Короче, суть в том, что suspend-функции — они как избранные, у них есть доступ к секретному клубу с приостановками. Обычные функции — это плебеи, им туда нельзя. Но ты всегда можешь купить им гостевой билет через launch или устроить дебош через runBlocking. Выбирай, но осторожно — ходишь по охуенно тонкому льду.