Kotlin CoroutinesJuniorTechnical
Что такое Flow в Kotlin и чем он отличается от suspend fun, возвращающей одно значение?
suspend fun возвращает ровно одно значение после завершения; Flow — асинхронный холодный поток, испускающий 0..N значений, который не стартует до вызова collect. Используйте suspend fun для разовых запросов, Flow — для потоков событий или реактивных обновлений.
Flow vs suspend fun: одно значение или поток
Suspend-функция, возвращающая одно значение, выполняется один раз и отдаёт результат — как обычный блокирующий вызов, но без блокировки потока. Flow — это асинхронный поток, который может испустить ноль, одно или много значений во времени, а затем завершиться нормально или с ошибкой.
suspend fun — одиночный результат
// Возвращает одно значение после завершения IO-операции
suspend fun fetchUser(id: Long): User {
return httpClient.get("https://api.example.com/users/$id")
}
// Вызов:
val user = fetchUser(42) // suspend fun, ждём одного User
println(user.name)
Функция стартует, делает работу и возвращает ровно одно значение (или бросает исключение). После возврата она «мертва».
Flow — поток значений
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.*
// Испускает новые цены акции каждые 500 мс
fun stockPrices(ticker: String): Flow<Double> = flow {
while (true) {
val price = fetchLatestPrice(ticker) // suspend
emit(price)
delay(500)
}
}
// Сборщик:
fun main() = runBlocking {
stockPrices("AAPL")
.take(5)
.collect { price -> println("Price: $price") }
}
Ключевые отличия
- Количество значений: suspend fun — ровно одно (или исключение); Flow — 0..N значений.
- Холодность: Flow холодный — код внутри
flow { }не выполняется до вызова терминального оператора (collect,first,toList). Каждый новый сборщик запускает Flow заново. - Время жизни: suspend fun живёт от вызова до возврата; Flow может существовать неограниченно долго (WebSocket, наблюдение за БД).
- Отмена: оба поддерживают отмену через
CancellationException; у Flow отмена распространяется на производителя через отмену вызывающего scope. - Обработка ошибок: у suspend fun — стандартный try/catch; у Flow — операторы
catch { }иonCompletion { }, плюс try/catch в collect.
Когда что выбирать
- Нужен один HTTP-ответ, результат SQL-запроса, итог вычисления → suspend fun.
- Нужны обновления в реальном времени, наблюдение за Room-базой, серия событий, пагинация → Flow.
- StateFlow/SharedFlow используются для разделяемого «горячего» состояния (замена LiveData в Android).
Пример: Room возвращает Flow
// DAO
@Dao
interface UserDao {
// suspend fun — разовый запрос
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getById(id: Long): User?
// Flow — реактивное наблюдение, переиспускается при изменении таблицы
@Query("SELECT * FROM users ORDER BY name")
fun observeAll(): Flow<List<User>>
}
Подводные камни
- Холодность забывается: если никто не вызывает
collect, код внутриflow { }никогда не выполнится — это ошибка, если вы ожидаете side-эффект (логирование, запись в БД) при создании Flow. - Несколько сборщиков = несколько запусков: два вызова
collectна одном холодном Flow запускают два отдельных «сеанса» — это удваивает сетевые вызовы. ИспользуйтеshareInилиstateInдля «горячего» разделения. - emit не thread-safe: нельзя вызывать
emitиз разных корутин одновременно безchannelFlow { }. - CancellationException нельзя проглатывать: внутри
flow { }catch-блок, поглощающий все исключения, сломает отмену корутины. - Бесконечный Flow утечёт: без
take(n),takeWhileили отмены scope бесконечный Flow (WebSocket, polling) продолжит работу вечно. - suspend fun нельзя вернуть из Flow-билдера напрямую: попытка возвращать suspend fun там, где ожидается Flow, приведёт к ошибке типов.
Common mistakes
- Объяснять «Flow и suspend fun с одним значением» только как синтаксис и не описывать поведение runtime/compiler.
- Игнорировать важный риск: Использование Flow для одного значения усложняет API, а suspend fun не подходит для stream progress/events.
- Давать пример без edge case: отмены, null, recomposition, platform boundary или ошибки.
What the interviewer is testing
- Формулирует суть темы «Flow и suspend fun с одним значением» своими словами и связывает ее с кодом.
- Называет механизм: Cold Flow начинает работу при collect и может применять operators между emission и collector.
- Видит production-последствие: Использование Flow для одного значения усложняет API, а suspend fun не подходит для stream progress/events.