suspend function: что это, синтаксис и работа в корутинах

Автор: IT Sectr Опубликовано: 2026-06-22 Время чтения: 9 мин

Suspend function — это функция с модификатором suspend, которая может приостанавливать своё выполнение без блокировки потока и возобновляться позже в той же корутине. По данным JetBrains Kotlin Docs, 2025, suspend-функции являются фундаментальным строительным блоком корутин, обеспечивающим асинхронность без callback-ов. Каждая suspend-функция компилируется в state-машину на базе Continuation, что позволяет эффективно управлять точками приостановки.

Главное

  • Suspend — ключевое слово Kotlin, которое помечает функцию как приостанавливаемую (асинхронную)
  • Continuation — скрытый параметр, который компилятор добавляет в каждую suspend-функцию для сохранения состояния
  • Точки приостановки — места вызова других suspend-функций, где корутина может остановиться без блокировки
  • State-машина — внутреннее представление suspend-функции, где каждая точка приостановки — отдельный state
  • Вызов только из корутины — suspend-функции можно вызывать только из другой suspend-функции или из launch/async

Что такое suspend function в Kotlin?

Suspend function — это функция, объявленная с ключевым словом suspend, которая может приостанавливать выполнение в одной или нескольких точках, не блокируя поток. Каждый вызов suspend-функции внутри другой suspend-функции является потенциальной точкой приостановки.

kotlin
suspend fun fetchUserData(): User {
    val response = httpClient.get("/user")
    return parser.parse(response)
}

Компилятор Kotlin транслирует такую функцию в state-машину. Каждая точка приостановки (вызов другой suspend-функции) становится состоянием (label). Текущий поток освобождается между состояниями, а после завершения ожидаемой операции выполнение возобновляется со следующего состояния.

История появления

Suspend-функции появились в Kotlin 1.3 (2018 год) вместе с корутинами как экспериментальная фича и стали стабильными в Kotlin 1.5 (2021 год). До этого асинхронность в Kotlin/Java обеспечивалась через callback-и, RxJava и CompletableFuture. Suspend-функции предложили альтернативу с линейным синтаксисом и автоматическим управлением потоками.

Как работают suspend-функции: Continuation и state-машина

Понимание внутреннего устройства suspend-функций — ключ к правильной работе с корутинами. В отличие от обычных функций, каждая suspend-функция компилируется в класс с интерфейсом Continuation.

Continuation — скрытый параметр

Компилятор Kotlin добавляет параметр типа Continuation в конец каждого параметра suspend-функции. Continuation содержит:

  • context — CoroutineContext (диспетчер, job, элементы контекста)
  • resumeWith — метод для возобновления выполнения с результатом или исключением
  • label — индекс текущего состояния в state-машине

State-машина на примере

Допустим, у нас есть suspend-функция с двумя вызовами других suspend-функций:

kotlin
suspend fun process() {
    val a = stepOne()
    val b = stepTwo(a)
    println(b)
}

Компилятор преобразует её в state-машину с метками:

kotlin
// Simplified generated code representation
fun process(cont: Continuation<Unit>): Any? {
    val cont = cont as ProcessContinuation
    when (cont.label) {
        0 -> {
            cont.label = 1
            if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        1 -> {
            cont.label = 2
            val a = cont.result as TypeA
            if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        2 -> {
            println(cont.result)
            Unit
        }
    }
}

Ключевое наблюдение: если функция возвращает COROUTINE_SUSPENDED, текущий поток освобождается. Когда асинхронная операция завершается, Continuation.resumeWith вызывается, и state-машина продолжает со следующего label.

Синтаксис suspend-функций: объявление и вызов

Объявление suspend-функции ничем не отличается от обычной, кроме ключевого слова suspend перед fun. Ограничение только одно: вызывать suspend-функцию можно только из корутины или другой suspend-функции.

Базовое объявление

kotlin
suspend fun delayAndReturn(ms: Long): String {
    delay(ms)
    return "Done after ${ms}ms"
}

В этом примере delay — тоже suspend-функция, которая приостанавливает корутину на указанное количество миллисекунд, не блокируя поток. После задержки выполнение возобновляется.

Вызов из корутины

kotlin
fun main() = runBlocking {
    val result = delayAndReturn(1000)
    println(result)
}

runBlocking создаёт мост между обычным миром и корутинами. Внутри лямбды доступен вызов любых suspend-функций.

Suspend-лямбды и функциональные типы

Kotlin поддерживает suspend-версии функциональных типов — suspend () -> T и suspend (A) -> B. Это позволяет передавать асинхронные лямбды в функции высшего порядка:

kotlin
suspend fun  withRetry(
    retries: Int = 3,
    block: suspend () -> T
): T {
    repeat(retries - 1) {
        try { return block() }
        catch (_: Exception) { delay(100) }
    }
    return block()
}

Функция withRetry принимает suspend-лямбду и повторяет её выполнение при ошибках. Это типичный паттерн для сетевых запросов с ретраями.

Чем suspend-функции отличаются от обычных

Различия между suspend и обычными функциями выходят за рамки простого добавления модификатора. Рассмотрим принципиальные отличия.

ХарактеристикаОбычная функцияSuspend-функция
Поток выполненияБлокирует поток до завершенияМожет освободить поток и возобновиться позже
Параметры компилятораТолько указанные параметрыНеявный Continuation в конце
Вызов из обычной функцииДаНет
СтекФизический стек потокаState-машина в heap + физический стек между точками
ВозвратНепосредственное значениеЗначение или COROUTINE_SUSPENDED
ПроизводительностьМинимальные накладные расходы~несколько наносекунд на state-машину (Kotlin 1.9+)

Почему suspend-функции нельзя вызывать из обычных

Обычная функция не имеет Continuation — ей некуда сохранить состояние и нечем возобновить выполнение. Если нужно вызвать suspend-функцию из обычной, используйте runBlocking (для тестов) или CoroutineScope.launch (для production с учётом lifecycle).

Примеры suspend-функций в Android

Рассмотрим три реальных сценария использования suspend-функций в Android-приложениях на Kotlin.

Пример 1: Room DAO с suspend-запросами

Room поддерживает suspend-функции напрямую — запрос выполняется в фоновом потоке автоматически:

kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUser(id: Int): User?

    @Insert
    suspend fun insertUser(user: User)
}

Room внутренне использует Dispatchers.IO для выполнения запроса, а результат возвращается в тот диспетчер, на котором была вызвана suspend-функция.

Пример 2: Композиция suspend-функций для загрузки экрана

kotlin
class ProfileViewModel : ViewModel() {
    private val repo = ProfileRepository()

    fun loadProfile(id: String) {
        viewModelScope.launch {
            val profile = repo.getProfile(id)
            _profile.value = profile
        }
    }
}

ViewModelScope.launch создаёт корутину, внутри которой вызывается suspend-функция getProfile. После получения результата UI обновляется на главном потоке.

Пример 3: Последовательные асинхронные шаги

kotlin
suspend fun placeOrder(cart: Cart): OrderResult {
    val validated = validateCart(cart)
    val payment = processPayment(validated)
    val receipt = sendReceipt(payment)
    return receipt
}

Три suspend-функции выполняются последовательно. На каждом шаге корутина может приостановиться, не блокируя поток. Если какой-то шаг выбросит исключение — остальные не выполнятся, что защищает от некорректных состояний заказа.

Типовые ошибки при работе с suspend-функциями

Даже опытные разработчики Kotlin допускают ошибки при проектировании suspend-функций. Рассмотрим самые частые.

Ошибка 1: Блокирующие вызовы внутри suspend

Suspend-функция не делает код автоматически асинхронным. Thread.sleep(), InputStream.read() и другие блокирующие вызовы всё равно заблокируют поток. Используйте withContext(Dispatchers.IO) для оборачивания блокирующих операций.

Ошибка 2: Создание suspend-функций без необходимости

Если функция не вызывает других suspend-функций и не выполняет асинхронных операций — модификатор suspend избыточен. Он добавляет накладные расходы на state-машину и ограничивает контекст вызова. Делайте функцию suspend только когда она действительно приостанавливается.

Ошибка 3: Игнорирование CancellationException

При отмене корутины suspend-функции выбрасывают CancellationException. Не ловите его бездумно — вы лишаете вызывающий код возможности корректно завершить отмену. Если нужно выполнить финализирующую операцию, используйте finally блок и NonCancellable.

kotlin
suspend fun safeOperation() {
    try {
        doWork()
    } finally {
        withContext(NonCancellable) {
            cleanup()
        }
    }
}

Блок finally выполняется всегда, включая отмену, а NonCancellable гарантирует, что cleanup не прервётся.

Ошибка 4: Вызов suspend-функций из callback-ов

Нельзя вызвать suspend-функцию напрямую из callback без создания корутины. Используйте suspendCoroutine или suspendCancellableCoroutine для адаптации callback-стиля к корутинам.

Часто задаваемые вопросы

Может ли suspend-функция не содержать точек приостановки?

Да, технически suspend-функция может не вызывать других suspend-функций. Компилятор создаст state-машину с одним состоянием (label 0). Однако практической пользы в такой функции нет — она выполняется как обычная, но с накладными расходами. Не используйте suspend без необходимости.

Как отлаживать suspend-функции?

Kotlin предоставляет kotlinx-coroutines-debug — библиотеку с DebugProbes и инструментами трассировки корутин. В Android Studio начиная с версии Arctic Fox есть встроенная вкладка Coroutines в Debugger, которая показывает активные корутины, их состояние и точки приостановки.

Влияет ли количество suspend-точек на производительность?

Каждая точка приостановки создаёт новое состояние в state-машине. Для большинства приложений накладные расходы одной точки составляют единицы наносекунд (Kotlin 1.9+). Только при десятках тысяч точек в цикле стоит объединять операции или использовать sequence/flow.

Чем suspend-функция отличается от async/await в других языках?

В Kotlin suspend — модификатор типа функции, а не маркер возвращаемого значения (как async в C#). Любая suspend-функция может иметь любые параметры и возвращаемый тип, и её вызов синтаксически не отличается от вызова обычной функции — нет await-оператора на месте вызова.

Как преобразовать callback-функцию в suspend?

Используйте suspendCancellableCoroutine для адаптации. Внутри вы передаёте регистрацию callback, которая вызывает continuation.resume(), и возвращаете токен отмены, если callback поддерживает отписку. Это стандартный паттерн для обёртки старых Android API.

Итоги

  • Suspend function — функция с модификатором suspend, которая может приостанавливать выполнение без блокировки потока через механизм Continuation
  • State-машина — внутреннее представление suspend-функции в байткоде Kotlin, где каждая точка приостановки — отдельное состояние с label
  • Continuation — скрытый параметр, содержащий контекст корутины и метод resumeWith для возобновления выполнения
  • Вызов только из корутины — suspend-функции недоступны из обычных функций без runBlocking или CoroutineScope
  • Блокирующие операции внутри suspend требуют withContext(Dispatchers.IO) — иначе поток блокируется
  • Room и Retrofit поддерживают suspend-функции нативно, автоматически управляя фоновыми потоками
  • CancellationException — обрабатывайте отмену через finally + NonCancellable, не ловите CancellationException бездумно

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также