suspend function: cos'è, sintassi e funzionamento nelle coroutine

Autore: IT Sectr Pubblicato: 2026-06-22 Tempo di lettura: 9 min

Suspend function è una funzione con il modificatore suspend che può sospendere la propria esecuzione senza bloccare un thread e riprendere più tardi nella stessa coroutine. Secondo JetBrains Kotlin Docs, 2025, le funzioni suspend sono un blocco fondamentale delle coroutine, fornendo asincronicità senza callback. Ogni funzione suspend viene compilata in una macchina a stati basata su Continuation, consentendo una gestione efficiente dei punti di sospensione.

Punti chiave

  • Suspend — parola chiave Kotlin che marca una funzione come sospendibile (asincrona)
  • Continuation — parametro nascosto che il compilatore aggiunge a ogni funzione suspend per salvare lo stato
  • Punti di sospensione — punti in cui vengono chiamate altre funzioni suspend, dove una coroutine può fermarsi senza bloccare
  • Macchina a stati — rappresentazione interna di una funzione suspend, dove ogni punto di sospensione è uno stato separato
  • Chiamata solo da coroutine — le funzioni suspend possono essere chiamate solo da un'altra funzione suspend o da launch/async

Cos'è una suspend function in Kotlin?

Suspend function è una funzione dichiarata con la parola chiave suspend che può sospendere l'esecuzione in uno o più punti senza bloccare il thread. Ogni chiamata a una funzione suspend all'interno di un'altra funzione suspend è un potenziale punto di sospensione.

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

Il compilatore Kotlin traduce tale funzione in una macchina a stati. Ogni punto di sospensione (una chiamata a un'altra funzione suspend) diventa uno stato (label). Il thread corrente viene rilasciato tra gli stati e, una completata l'operazione attesa, l'esecuzione riprende dallo stato successivo.

Storia

Le funzioni suspend sono apparse in Kotlin 1.3 (2018) insieme alle coroutine come funzionalità sperimentale e sono diventate stabili in Kotlin 1.5 (2021). Prima di allora, l'asincronicità in Kotlin/Java era ottenuta tramite callback, RxJava e CompletableFuture. Le funzioni suspend hanno offerto un'alternativa con sintassi lineare e gestione automatica dei thread.

Come funzionano le funzioni suspend: Continuation e macchina a stati

Comprendere il funzionamento interno delle funzioni suspend è la chiave per lavorare correttamente con le coroutine. A differenza delle funzioni normali, ogni funzione suspend viene compilata in una classe con l'interfaccia Continuation.

Continuation — il parametro nascosto

Il compilatore Kotlin aggiunge un parametro di tipo Continuation alla fine di ogni elenco di parametri di una funzione suspend. Continuation contiene:

  • context — CoroutineContext (dispatcher, job, elementi del contesto)
  • resumeWith — metodo per riprendere l'esecuzione con un risultato o un'eccezione
  • label — indice dello stato corrente nella macchina a stati

Esempio di macchina a stati

Supponiamo di avere una funzione suspend con due chiamate ad altre funzioni suspend:

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

Il compilatore la trasforma in una macchina a stati con etichette:

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
        }
    }
}

Osservazione chiave: se la funzione restituisce COROUTINE_SUSPENDED, il thread corrente viene rilasciato. Quando l'operazione asincrona si completa, viene chiamato Continuation.resumeWith e la macchina a stati continua dall'etichetta successiva.

Sintassi delle funzioni suspend: dichiarazione e invocazione

Dichiarare una funzione suspend non è diverso da una funzione normale, eccetto per la parola chiave suspend prima di fun. C'è solo una restrizione: una funzione suspend può essere chiamata solo da una coroutine o da un'altra funzione suspend.

Dichiarazione di base

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

In questo esempio, delay è anch'essa una funzione suspend che sospende la coroutine per il numero specificato di millisecondi senza bloccare il thread. Dopo il ritardo, l'esecuzione riprende.

Chiamata da una coroutine

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

runBlocking crea un ponte tra il mondo normale e le coroutine. All'interno del lambda, qualsiasi funzione suspend può essere chiamata.

Lambda suspend e tipi funzionali

Kotlin supporta versioni suspend dei tipi funzionali — suspend () -> T e suspend (A) -> B. Ciò consente di passare lambda asincrone a funzioni di ordine superiore:

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

La funzione withRetry accetta un lambda suspend e ne riprova l'esecuzione in caso di errore. Questo è un modello tipico per richieste di rete con tentativi.

Differenze tra funzioni suspend e funzioni normali

Le differenze tra funzioni suspend e normali vanno oltre la semplice aggiunta di un modificatore. Esaminiamo le distinzioni principali.

CaratteristicaFunzione normaleFunzione suspend
Thread di esecuzioneBlocca il thread fino al completamentoPuò rilasciare il thread e riprendere dopo
Parametri del compilatoreSolo i parametri specificatiContinuation implicito alla fine
Chiamata da funzione normaleNo
StackStack fisico del threadMacchina a stati nell'heap + stack fisico tra i punti
Valore restituitoValore direttoValore o COROUTINE_SUSPENDED
PrestazioniOverhead minimo~pochi nanosecondi per macchina a stati (Kotlin 1.9+)

Perché le funzioni suspend non possono essere chiamate da funzioni normali

Una funzione normale non ha un Continuation — non ha un posto dove salvare lo stato e nulla con cui riprendere l'esecuzione. Se devi chiamare una funzione suspend da una funzione normale, usa runBlocking (per i test) o CoroutineScope.launch (per la produzione con consapevolezza del ciclo di vita).

Esempi di funzioni suspend in Android

Esaminiamo tre scenari reali di utilizzo delle funzioni suspend nelle app Android con Kotlin.

Esempio 1: Room DAO con query suspend

Room supporta le funzioni suspend direttamente — la query viene eseguita automaticamente su un thread in background:

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

    @Insert
    suspend fun insertUser(user: User)
}

Room utilizza internamente Dispatchers.IO per eseguire la query e il risultato viene restituito sul dispatcher in cui è stata chiamata la funzione suspend.

Esempio 2: Composizione di funzioni suspend per il caricamento della schermata

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

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

ViewModelScope.launch crea una coroutine, all'interno della quale viene chiamata la funzione suspend getProfile. Dopo aver ottenuto il risultato, l'interfaccia utente viene aggiornata sul thread principale.

Esempio 3: Passaggi asincroni sequenziali

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

Tre funzioni suspend vengono eseguite sequenzialmente. A ogni passaggio, la coroutine può sospendersi senza bloccare il thread. Se un passaggio genera un'eccezione, i passaggi rimanenti non vengono eseguiti, proteggendo da stati di ordine errati.

Errori comuni con le funzioni suspend

Anche gli sviluppatori Kotlin esperti commettono errori nella progettazione di funzioni suspend. Esaminiamo i più comuni.

Errore 1: Chiamate bloccanti all'interno di suspend

Una funzione suspend non rende il codice automaticamente asincrono. Thread.sleep(), InputStream.read() e altre chiamate bloccanti continueranno a bloccare il thread. Usa withContext(Dispatchers.IO) per incapsulare le operazioni bloccanti.

Errore 2: Creare funzioni suspend senza necessità

Se una funzione non chiama altre funzioni suspend e non esegue operazioni asincrone — il modificatore suspend è ridondante. Aggiunge overhead alla macchina a stati e limita il contesto di chiamata. Rendi una funzione suspend solo quando effettivamente si sospende.

Errore 3: Ignorare CancellationException

Quando una coroutine viene annullata, le funzioni suspend lanciano CancellationException. Non catturarla sconsideratamente — stai privando il codice chiamante della capacità di completare correttamente l'annullamento. Se devi eseguire un'operazione di finalizzazione, usa un blocco finally e NonCancellable.

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

Il blocco finally viene sempre eseguito, incluso in caso di annullamento, e NonCancellable garantisce che la pulizia non venga interrotta.

Errore 4: Chiamare funzioni suspend dai callback

Non puoi chiamare una funzione suspend direttamente da un callback senza creare una coroutine. Usa suspendCoroutine o suspendCancellableCoroutine per adattare lo stile callback alle coroutine.

Domande frequenti

Una funzione suspend può non avere punti di sospensione?

Sì, tecnicamente una funzione suspend può non chiamare altre funzioni suspend. Il compilatore creerà una macchina a stati con un unico stato (label 0). Tuttavia, non c'è alcun beneficio pratico in tale funzione — viene eseguita come una normale ma con overhead. Non usare suspend inutilmente.

Come eseguire il debug delle funzioni suspend?

Kotlin fornisce kotlinx-coroutines-debug — una libreria con DebugProbes e strumenti di tracciamento delle coroutine. In Android Studio a partire da Arctic Fox, c'è una scheda Coroutines integrata nel Debugger che mostra le coroutine attive, il loro stato e i punti di sospensione.

Il numero di punti di sospensione influisce sulle prestazioni?

Ogni punto di sospensione crea un nuovo stato nella macchina a stati. Per la maggior parte delle applicazioni, l'overhead di un punto è di pochi nanosecondi (Kotlin 1.9+). Solo con decine di migliaia di punti in un ciclo dovresti considerare di combinare operazioni o usare sequence/flow.

In cosa differisce una funzione suspend da async/await in altri linguaggi?

In Kotlin, suspend è un modificatore di tipo di funzione, non un marcatore di valore restituito (come async in C#). Qualsiasi funzione suspend può avere qualsiasi parametro e tipo restituito, e la sua chiamata sintatticamente non differisce da una chiamata di funzione normale — non c'è un operatore await nel punto di chiamata.

Come convertire una funzione callback in suspend?

Usa suspendCancellableCoroutine per l'adattamento. All'interno, registri un callback che chiama continuation.resume() e restituisci un token di annullamento se il callback supporta la disiscrizione. Questo è il modello standard per incapsulare vecchie API Android.

Riepilogo

  • Suspend function — funzione con il modificatore suspend che può sospendere l'esecuzione senza bloccare un thread tramite il meccanismo Continuation
  • Macchina a stati — rappresentazione interna di una funzione suspend nel bytecode Kotlin, dove ogni punto di sospensione è uno stato separato con un'etichetta
  • Continuation — parametro nascosto contenente il contesto della coroutine e il metodo resumeWith per riprendere l'esecuzione
  • Chiamata solo da coroutine — le funzioni suspend non sono accessibili da funzioni normali senza runBlocking o CoroutineScope
  • Operazioni bloccanti all'interno di suspend richiedono withContext(Dispatchers.IO) — altrimenti il thread si blocca
  • Room e Retrofit supportano nativamente le funzioni suspend, gestendo automaticamente i thread in background
  • CancellationException — gestisci l'annullamento tramite finally + NonCancellable, non catturare CancellationException sconsideratamente

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche