Как дождаться выполнения корутины созданной с помощью launch

Ответ

В Kotlin можно дождаться завершения корутины несколькими способами:

  1. Использование join():
val job = launch {
    // какой-то код
}
job.join() // ждем завершения
  1. Использование runBlocking (для тестов или main-функций):
runBlocking {
    launch {
        // какой-то код
    }
    // автоматическое ожидание всех корутин внутри блока
}
  1. Для нескольких корутин - joinAll():
val job1 = launch { /* ... */ }
val job2 = launch { /* ... */ }
joinAll(job1, job2)

Важно: join() - suspend-функция, поэтому должна вызываться из корутины или другого suspend-контекста. Не используйте join() в UI-потоке без runBlocking, это приведет к ANR. Для UI лучше использовать async/await или колбэки.

Ответ 18+ 🔞

Э, слушай, а вот тут у меня как раз вопрос назрел, про эти ваши корутины. Сижу я, значит, думаю: ну запустил я эту асинхронную хрень, а как потом дождаться-то, чтобы она не сбежала, как последняя мартышлюшка, пока я чай пью? Оказалось, вариантов — овердохуища, но главное — не накосячить.

Ну, самый простой способ, прям в лоб — это join(). Запустил корутину, она тебе Job вернула, и ты её как собаку на поводок взял: «Сиди тут, пока не закончишь!». Выглядит это, ёпта, примерно так:

val job = launch {
    // какой-то код
}
job.join() // ждем завершения

Вот только подвох есть, чувак: join() — это suspend-функция. Это значит, что вызывать её надо либо из другой корутины, либо из какого-нибудь suspend-контекста. Если ты её в UI-потоке ляпнешь голую, без runBlocking, то твое приложение просто встанет колом, как вкопанное, и пользователь тебе такое ANR покажет, что ты сам от себя охуеешь. Доверия к такому коду — ебать ноль.

Поэтому для каких-нибудь тестов или в main() функции есть второй способ — runBlocking. Это как загнать всех в один сарай и не выпускать, пока все работы не сделают.

runBlocking {
    launch {
        // какой-то код
    }
    // автоматическое ожидание всех корутин внутри блока
}

Удобно, конечно, но в нормальном андроид-приложении совать runBlocking в UI-поток — это всё равно что пытаться засунуть хуй в пальто: вроде и можно, но выглядит идиотски и всем неудобно.

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

val job1 = launch { /* ... */ }
val job2 = launch { /* ... */ }
joinAll(job1, job2)

Короче, суть в чём: если ты не в UI-потоке ошиваешься, то join() — норм. А для UI, чувак, будь умнее — юзай async с await или старые добрые колбэки. Не надо тут волнение ебать и всё блокировать. Выбирай инструмент по уму, а не как попало.