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-функције, где је свака тачка суспензије засебно стање
  • Позив само из корутине — 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
// Поједностављена репрезентација генерисаног кода
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 ствара мост између обичног света и корутина. Унутар lambda-е дозвољен је позив било којих suspend-функција.

Suspend lambda-е и функционални типови

Kotlin подржава suspend верзије функционалних типова — suspend () -> T и suspend (A) -> B. Ово омогућава прослеђивање асинхроних lambda-а функцијама вишег реда:

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 lambda-у и понавља њено извршење при грешкама. Ово је типичан образац за мрежне захтеве са понављањем.

По чему се suspend-функције разликују од обичних

Разлике између suspend и обичних функција превазилазе просто додавање модификатора. Размотримо суштинске разлике.

КарактеристикаОбична функцијаSuspend-функција
Нит извршењаБлокира нит до завршеткаМоже ослободити нит и наставити касније
Параметри компајлераСамо наведени параметриИмплицитни Continuation на крају
Позив из обичне функцијеДаНе
СтекФизички стек нитиState-машина на heap-у + физички стек између тачака
ПовратакНепосредна вредностВредност или COROUTINE_SUSPENDED
ПерформансеМинимални додатни трошкови~неколико наносекунди за state-машину (Kotlin 1.9+)

Зашто се suspend-функције не могу позивати из обичних

Обична функција нема Continuation — нема где да сачува стање и чиме да настави извршење. Ако треба да позовете suspend-функцију из обичне, користите runBlocking (за тестове) или CoroutineScope.launch (за продукцију узимајући у обзир 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 гарантује да се чишћење неће прекинути.

Грешка 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође