Suspend function es una función con el modificador suspend que puede pausar su ejecución sin bloquear un hilo y reanudarse más tarde en la misma corutina. Según JetBrains Kotlin Docs, 2025, las funciones suspend son un bloque fundamental de las corutinas, proporcionando asincronía sin callbacks. Cada función suspend se compila en una máquina de estados basada en Continuation, permitiendo una gestión eficiente de los puntos de suspensión.
Puntos clave
Suspend function es una función declarada con la palabra clave suspend que puede pausar la ejecución en uno o más puntos sin bloquear el hilo. Cada llamada a una función suspend dentro de otra función suspend es un punto de suspensión potencial.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
El compilador de Kotlin traduce dicha función en una máquina de estados. Cada punto de suspensión (una llamada a otra función suspend) se convierte en un estado (label). El hilo actual se libera entre estados, y cuando la operación esperada se completa, la ejecución se reanuda desde el siguiente estado.
Las funciones suspend aparecieron en Kotlin 1.3 (2018) junto con las corutinas como característica experimental y se volvieron estables en Kotlin 1.5 (2021). Antes de eso, la asincronía en Kotlin/Java se lograba mediante callbacks, RxJava y CompletableFuture. Las funciones suspend ofrecieron una alternativa con sintaxis lineal y gestión automática de hilos.
Comprender el funcionamiento interno de las funciones suspend es la clave para trabajar correctamente con corutinas. A diferencia de las funciones normales, cada función suspend se compila en una clase con la interfaz Continuation.
El compilador de Kotlin añade un parámetro de tipo Continuation al final de cada lista de parámetros de una función suspend. Continuation contiene:
Supongamos que tenemos una función suspend con dos llamadas a otras funciones suspend:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
El compilador la transforma en una máquina de estados con etiquetas:
// 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
}
}
}
Observación clave: si la función devuelve COROUTINE_SUSPENDED, el hilo actual se libera. Cuando la operación asíncrona se completa, se llama a Continuation.resumeWith y la máquina de estados continúa desde la siguiente etiqueta.
Declarar una función suspend no es diferente de una función normal, excepto por la palabra clave suspend antes de fun. Solo hay una restricción: una función suspend solo puede llamarse desde una corutina u otra función suspend.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
En este ejemplo, delay también es una función suspend que suspende la corutina durante el número especificado de milisegundos sin bloquear el hilo. Después de la pausa, la ejecución se reanuda.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking crea un puente entre el mundo normal y las corutinas. Dentro de la lambda, se puede llamar a cualquier función suspend.
Kotlin admite versiones suspend de tipos funcionales — suspend () -> T y suspend (A) -> B. Esto permite pasar lambdas asíncronas a funciones de orden superior:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
La función withRetry recibe una lambda suspend y reintenta su ejecución en caso de error. Este es un patrón típico para solicitudes de red con reintentos.
Las diferencias entre funciones suspend y normales van más allá de simplemente añadir un modificador. Veamos las diferencias clave.
| Característica | Función normal | Función suspend |
|---|---|---|
| Hilo de ejecución | Bloquea el hilo hasta que termina | Puede liberar el hilo y reanudarse después |
| Parámetros del compilador | Solo los parámetros especificados | Continuation implícito al final |
| Llamada desde función normal | Sí | No |
| Pila | Pila física del hilo | Máquina de estados en heap + pila física entre puntos |
| Valor de retorno | Valor directo | Valor o COROUTINE_SUSPENDED |
| Rendimiento | Sobrecarga mínima | ~unos nanosegundos por máquina de estados (Kotlin 1.9+) |
Una función normal no tiene un Continuation — no tiene dónde guardar el estado ni con qué reanudar la ejecución. Si necesitas llamar a una función suspend desde una normal, usa runBlocking (para pruebas) o CoroutineScope.launch (para producción teniendo en cuenta el ciclo de vida).
Veamos tres escenarios reales de uso de funciones suspend en aplicaciones Android con Kotlin.
Room admite funciones suspend directamente — la consulta se ejecuta en un hilo en segundo plano automáticamente:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room internamente usa Dispatchers.IO para ejecutar la consulta, y el resultado se devuelve en el dispatcher donde se llamó a la función suspend.
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 corutina, dentro de la cual se llama a la función suspend getProfile. Después de obtener el resultado, la interfaz se actualiza en el hilo principal.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Tres funciones suspend se ejecutan secuencialmente. En cada paso, la corutina puede suspenderse sin bloquear el hilo. Si algún paso lanza una excepción, los pasos restantes no se ejecutan, protegiendo contra estados de pedido incorrectos.
Incluso los desarrolladores experimentados de Kotlin cometen errores al diseñar funciones suspend. Veamos los más comunes.
Una función suspend no hace que el código sea automáticamente asíncrono. Thread.sleep(), InputStream.read() y otras llamadas bloqueantes seguirán bloqueando el hilo. Usa withContext(Dispatchers.IO) para envolver operaciones bloqueantes.
Si una función no llama a otras funciones suspend ni realiza operaciones asíncronas — el modificador suspend es redundante. Añade sobrecarga a la máquina de estados y restringe el contexto de llamada. Haz que una función sea suspend solo cuando realmente se suspenda.
Cuando se cancela una corutina, las funciones suspend lanzan CancellationException. No la captures sin pensar — le quitas al código llamante la capacidad de completar correctamente la cancelación. Si necesitas realizar una operación de finalización, usa un bloque finally y NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
El bloque finally siempre se ejecuta, incluso en la cancelación, y NonCancellable garantiza que la limpieza no se interrumpa.
No puedes llamar a una función suspend directamente desde un callback sin crear una corutina. Usa suspendCoroutine o suspendCancellableCoroutine para adaptar el estilo de callback a las corutinas.
Preguntas frecuentes
Sí, técnicamente una función suspend puede no llamar a otras funciones suspend. El compilador creará una máquina de estados con un solo estado (label 0). Sin embargo, no hay ningún beneficio práctico en tal función — se ejecuta como una normal pero con sobrecarga. No uses suspend innecesariamente.
Kotlin proporciona kotlinx-coroutines-debug — una biblioteca con DebugProbes y herramientas de trazado de corutinas. En Android Studio a partir de Arctic Fox, hay una pestaña Coroutines incorporada en el Depurador que muestra las corutinas activas, su estado y los puntos de suspensión.
Cada punto de suspensión crea un nuevo estado en la máquina de estados. Para la mayoría de las aplicaciones, la sobrecarga de un punto es de unos pocos nanosegundos (Kotlin 1.9+). Solo con decenas de miles de puntos en un bucle deberías considerar combinar operaciones o usar sequence/flow.
En Kotlin, suspend es un modificador de tipo de función, no un marcador de valor de retorno (como async en C#). Cualquier función suspend puede tener cualquier parámetro y tipo de retorno, y su llamada sintácticamente no difiere de una llamada a una función normal — no hay un operador await en el lugar de la llamada.
Usa suspendCancellableCoroutine para la adaptación. Dentro, registras un callback que llama a continuation.resume(), y devuelves un token de cancelación si el callback admite cancelación de suscripción. Este es el patrón estándar para envolver APIs antiguas de Android.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también