suspend function: co to jest, składnia i działanie w korutynach

Autor: IT Sectr Opublikowano: 2026-06-22 Czas czytania: 9 min

Suspend function — to funkcja z modyfikatorem suspend, która może wstrzymywać swoje wykonanie bez blokowania wątku i wznawiać je później w tej samej korutynie. Według JetBrains Kotlin Docs, 2025, funkcje suspendujące są fundamentalnym budulcem korutyn, zapewniającym asynchroniczność bez callbacków. Każda funkcja suspendująca jest kompilowana do maszyny stanów opartej na Continuation, co pozwala efektywnie zarządzać punktami wstrzymania.

Najważniejsze

  • Suspend — słowo kluczowe Kotlin, które oznacza funkcję jako wstrzymywaną (asynchroniczną)
  • Continuation — ukryty parametr, który kompilator dodaje do każdej funkcji suspendującej w celu zachowania stanu
  • Punkty wstrzymania — miejsca wywołania innych funkcji suspendujących, gdzie korutyna może się zatrzymać bez blokowania
  • Maszyna stanów — wewnętrzna reprezentacja funkcji suspendującej, gdzie każdy punkt wstrzymania to osobny stan
  • Wywołanie tylko z korutyny — funkcje suspendujące można wywoływać tylko z innej funkcji suspendującej lub z launch/async

Czym jest suspend function w Kotlin?

Suspend function — to funkcja zadeklarowana ze słowem kluczowym suspend, która może wstrzymywać wykonanie w jednym lub kilku punktach bez blokowania wątku. Każde wywołanie funkcji suspendującej wewnątrz innej funkcji suspendującej jest potencjalnym punktem wstrzymania.

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

Kompilator Kotlin tłumaczy taką funkcję na maszynę stanów. Każdy punkt wstrzymania (wywołanie innej funkcji suspendującej) staje się stanem (label). Bieżący wątek jest zwalniany między stanami, a po zakończeniu oczekiwanej operacji wykonanie wznawia się od następnego stanu.

Historia powstania

Funkcje suspendujące pojawiły się w Kotlin 1.3 (2018 rok) wraz z korutynami jako eksperymentalna funkcja i stały się stabilne w Kotlin 1.5 (2021 rok). Wcześniej asynchroniczność w Kotlin/Java była zapewniana przez callbacki, RxJava i CompletableFuture. Funkcje suspendujące zaproponowały alternatywę z liniową składnią i automatycznym zarządzaniem wątkami.

Jak działają funkcje suspendujące: Continuation i maszyna stanów

Zrozumienie wewnętrznej budowy funkcji suspendujących to klucz do prawidłowej pracy z korutynami. W przeciwieństwie do zwykłych funkcji, każda funkcja suspendująca jest kompilowana do klasy z interfejsem Continuation.

Continuation — ukryty parametr

Kompilator Kotlin dodaje parametr typu Continuation na końcu każdego parametru funkcji suspendującej. Continuation zawiera:

  • context — CoroutineContext (dyspozytor, job, elementy kontekstu)
  • resumeWith — metoda do wznawiania wykonania z wynikiem lub wyjątkiem
  • label — indeks bieżącego stanu w maszynie stanów

Maszyna stanów na przykładzie

Zakładając, że mamy funkcję suspendującą z dwoma wywołaniami innych funkcji suspendujących:

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

Kompilator przekształca ją w maszynę stanów z etykietami:

kotlin
// Uproszczona reprezentacja wygenerowanego kodu
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
        }
    }
}

Kluczowe spostrzeżenie: jeśli funkcja zwraca COROUTINE_SUSPENDED, bieżący wątek jest zwalniany. Gdy operacja asynchroniczna się zakończy, wywoływane jest Continuation.resumeWith, a maszyna stanów kontynuuje od następnego labelu.

Składnia funkcji suspendujących: deklaracja i wywołanie

Deklaracja funkcji suspendującej niczym nie różni się od zwykłej, poza słowem kluczowym suspend przed fun. Ograniczenie jest tylko jedno: funkcję suspendującą można wywołać tylko z korutyny lub innej funkcji suspendującej.

Podstawowa deklaracja

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

W tym przykładzie delay to także funkcja suspendująca, która wstrzymuje korutynę na określoną liczbę milisekund bez blokowania wątku. Po opóźnieniu wykonanie zostaje wznowione.

Wywołanie z korutyny

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

runBlocking tworzy most między zwykłym światem a korutynami. Wewnątrz lambdy dostępne jest wywoływanie dowolnych funkcji suspendujących.

Lambdy suspendujące i typy funkcyjne

Kotlin obsługuje suspendujące wersje typów funkcyjnych — suspend () -> T i suspend (A) -> B. Pozwala to przekazywać asynchroniczne lambdy do funkcji wyższego rzędu:

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

Funkcja withRetry przyjmuje lambdę suspendującą i powtarza jej wykonanie w przypadku błędów. To typowy wzorzec dla zapytań sieciowych z ponawianiem.

Czym różnią się funkcje suspendujące od zwykłych

Różnice między suspend a zwykłymi funkcjami wykraczają poza proste dodanie modyfikatora. Rozważmy zasadnicze różnice.

CechaZwykła funkcjaFunkcja suspendująca
Wątek wykonaniaBlokuje wątek do zakończeniaMoże zwolnić wątek i wznowić później
Parametry kompilatoraTylko określone parametryNiejawny Continuation na końcu
Wywołanie ze zwykłej funkcjiTakNie
StosFizyczny stos wątkuMaszyna stanów na stercie + fizyczny stos między punktami
ZwracanieBezpośrednia wartośćWartość lub COROUTINE_SUSPENDED
WydajnośćMinimalne narzuty~kilka nanosekund na maszynę stanów (Kotlin 1.9+)

Dlaczego funkcji suspendujących nie można wywoływać ze zwykłych

Zwykła funkcja nie ma Continuation — nie ma gdzie zapisać stanu ani czym wznowić wykonania. Jeśli potrzebujesz wywołać funkcję suspendującą ze zwykłej, użyj runBlocking (do testów) lub CoroutineScope.launch (dla produkcji z uwzględnieniem lifecycle).

Przykłady funkcji suspendujących w Android

Rozważmy trzy rzeczywiste scenariusze użycia funkcji suspendujących w aplikacjach Android w Kotlin.

Przykład 1: Room DAO z zapytaniami suspendującymi

Room obsługuje funkcje suspendujące bezpośrednio — zapytanie jest wykonywane w tle automatycznie:

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

    @Insert
    suspend fun insertUser(user: User)
}

Room wewnętrznie używa Dispatchers.IO do wykonania zapytania, a wynik jest zwracany do tego dyspozytora, w którym została wywołana funkcja suspendująca.

Przykład 2: Kompozycja funkcji suspendujących do ładowania ekranu

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

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

ViewModelScope.launch tworzy korutynę, wewnątrz której wywoływana jest funkcja suspendująca getProfile. Po uzyskaniu wyniku interfejs jest aktualizowany na głównym wątku.

Przykład 3: Sekwencyjne kroki asynchroniczne

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

Trzy funkcje suspendujące są wykonywane sekwencyjnie. Na każdym kroku korutyna może się zatrzymać bez blokowania wątku. Jeśli któryś krok zgłosi wyjątek — pozostałe nie zostaną wykonane, co chroni przed nieprawidłowymi stanami zamówienia.

Typowe błędy przy pracy z funkcjami suspendującymi

Nawet doświadczeni programiści Kotlin popełniają błędy przy projektowaniu funkcji suspendujących. Rozważmy najczęstsze.

Błąd 1: Wywołania blokujące wewnątrz suspend

Funkcja suspendująca nie czyni kodu automatycznie asynchronicznym. Thread.sleep(), InputStream.read() i inne wywołania blokujące nadal zablokują wątek. Używaj withContext(Dispatchers.IO) do opakowywania operacji blokujących.

Błąd 2: Tworzenie funkcji suspendujących bez potrzeby

Jeśli funkcja nie wywołuje innych funkcji suspendujących i nie wykonuje operacji asynchronicznych — modyfikator suspend jest zbędny. Dodaje narzuty na maszynę stanów i ogranicza kontekst wywołania. Używaj modyfikatora suspend tylko gdy funkcja naprawdę się wstrzymuje.

Błąd 3: Ignorowanie CancellationException

Przy anulowaniu korutyny funkcje suspendujące rzucają CancellationException. Nie łap go bezmyślnie — pozbawiasz kod wywołujący możliwości poprawnego zakończenia anulowania. Jeśli potrzebujesz wykonać operację finalizującą, użyj bloku finally i NonCancellable.

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

Blok finally wykonuje się zawsze, włączając anulowanie, a NonCancellable gwarantuje, że cleanup nie zostanie przerwany.

Błąd 4: Wywoływanie funkcji suspendujących z callbacków

Nie można wywołać funkcji suspendującej bezpośrednio z callbacka bez utworzenia korutyny. Używaj suspendCoroutine lub suspendCancellableCoroutine do adaptacji stylu callbackowego do korutyn.

Często zadawane pytania

Czy funkcja suspendująca może nie zawierać punktów wstrzymania?

Tak, technicznie funkcja suspendująca może nie wywoływać innych funkcji suspendujących. Kompilator utworzy maszynę stanów z jednym stanem (label 0). Jednak praktycznej korzyści z takiej funkcji nie ma — wykonuje się jak zwykła, ale z narzutami. Nie używaj suspend bez potrzeby.

Jak debugować funkcje suspendujące?

Kotlin udostępnia kotlinx-coroutines-debug — bibliotekę z DebugProbes i narzędziami do śledzenia korutyn. W Android Studio od wersji Arctic Fox dostępna jest wbudowana zakładka Coroutines w Debugger, która pokazuje aktywne korutyny, ich stan i punkty wstrzymania.

Czy liczba punktów suspend wpływa na wydajność?

Każdy punkt wstrzymania tworzy nowy stan w maszynie stanów. Dla większości aplikacji narzut jednego punktu to jednostki nanosekund (Kotlin 1.9+). Dopiero przy dziesiątkach tysięcy punktów w pętli warto łączyć operacje lub używać sequence/flow.

Czym różni się funkcja suspendująca od async/await w innych językach?

W Kotlin suspend to modyfikator typu funkcji, a nie znacznik zwracanej wartości (jak async w C#). Każda funkcja suspendująca może mieć dowolne parametry i typ zwracany, a jej wywołanie składniowo nie różni się od wywołania zwykłej funkcji — nie ma operatora await w miejscu wywołania.

Jak przekształcić funkcję callbackową w suspend?

Użyj suspendCancellableCoroutine do adaptacji. Wewnątrz przekazujesz rejestrację callbacka, która wywołuje continuation.resume(), i zwracasz token anulowania, jeśli callback obsługuje rezygnację z subskrypcji. To standardowy wzorzec do opakowywania starych API Androida.

Podsumowanie

  • Suspend function — funkcja z modyfikatorem suspend, która może wstrzymywać wykonanie bez blokowania wątku przez mechanizm Continuation
  • Maszyna stanów — wewnętrzna reprezentacja funkcji suspendującej w bajtkodzie Kotlin, gdzie każdy punkt wstrzymania to osobny stan z labelem
  • Continuation — ukryty parametr zawierający kontekst korutyny i metodę resumeWith do wznawiania wykonania
  • Wywołanie tylko z korutyny — funkcje suspendujące są niedostępne ze zwykłych funkcji bez runBlocking lub CoroutineScope
  • Operacje blokujące wewnątrz suspend wymagają withContext(Dispatchers.IO) — w przeciwnym razie wątek zostanie zablokowany
  • Room i Retrofit obsługują funkcje suspendujące natywnie, automatycznie zarządzając wątkami tła
  • CancellationException — obsługuj anulowanie przez finally + NonCancellable, nie łap CancellationException bezmyślnie

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również