¿Qué se esconde detrás de la palabra clave suspend en Kotlin?
\n\nSi alguna vez has escrito en Kotlin y has intentado llamar a una función marcada con el modificador suspend, pero sin una corutina, seguramente obtuviste un error de compilación. Y esto no es un error, sino una característica. Las funciones suspend son la base de la programación asíncrona en Kotlin. En 2025 se han vuelto aún más cómodas, pero las preguntas de los principiantes (y también de los desarrolladores experimentados) no han desaparecido. Una de las más populares en Stack Overflow — «What does a suspend function mean in Kotlin coroutines?» — ha acumulado miles de visitas. Aclaremos esto de una vez por todas.
Una función suspend no es magia, es un contrato
\n\nImagina que pides una pizza. Llamas al restaurante, haces el pedido y, en lugar de esperar al teléfono 20 minutos, continúas con tus asuntos. Cuando la pizza está lista, te llaman — «regresas» a la conversación. Eso es una función suspend: puede pausar su ejecución sin bloquear el hilo, y luego reanudarse cuando el resultado esté disponible.
\n\nTécnicamente, suspend no es una palabra clave que haga que una función sea asíncrona «de la nada». Es un contrato: la función puede pausarse, pero no está obligada a hacerlo. El compilador de Kotlin convierte dicha función en una máquina de estados que sabe cómo guardar y restaurar su estado.
¿Cómo se ve una función suspend en el código?
\n\nAquí tienes un ejemplo sencillo:
\n\nsuspend fun fetchUserData(userId: String): User {\n delay(1000) // simulación de una solicitud de red\n return User(userId, "Alexei")\n}\n\nNo puedes llamar a fetchUserData así sin más, fuera de una corutina:
// ¡Error de compilación!\nfun main() {\n val user = fetchUserData("123") // Suspend function 'fetchUserData' should be called only from a coroutine or another suspend function\n}\n\nPara llamarla, debes estar dentro de una corutina. Por ejemplo:
\n\nfun main() = runBlocking {\n val user = fetchUserData("123")\n println(user.name) // Alexei\n}\n\nrunBlocking es un puente entre el mundo normal y el mundo de las corutinas. En proyectos reales, casi nunca lo usarás en producción (excepto en pruebas y en main). En su lugar, iniciarás corutinas a través de CoroutineScope.
¿Qué sucede «bajo el capó»?
\n\nCuando el compilador ve suspend, genera un parámetro adicional: un objeto Continuation. Este objeto almacena el punto en el que la corutina fue pausada y todas las variables locales. Gracias a esto, la corutina puede «dormirse» en un hilo y «despertarse» en otro, sin bloqueo.
Importante: una función suspend no bloquea el hilo. Mientras «duerme» (por ejemplo, esperando una respuesta del servidor), el hilo puede ejecutar otras tareas. Esta es una diferencia clave con los enfoques antiguos (Thread.sleep, callback hell).
\n\nMejores prácticas para usar funciones suspend en 2025
\n\nEn los últimos años, el ecosistema de Kotlin ha crecido enormemente. Han aparecido nuevas bibliotecas, y el compilador y el IDE han mejorado. Esto es lo que debes saber y aplicar ahora mismo.
\n\n1. No hagas funciones suspend que no se pausen
\n\nSi una función no contiene llamadas a otras funciones suspend (por ejemplo, delay, withContext, llamadas a API a través de Ktor/Retrofit), no le pongas suspend. Esto es engañoso y añade una sobrecarga innecesaria.
// Mal\nsuspend fun formatName(first: String, last: String): String {\n return "$first $last".trim()\n}\n\n// Bien\nfun formatName(first: String, last: String): String {\n return "$first $last".trim()\n}\n\n2. Usa withContext para cambiar de dispatcher
\n\nSi necesitas realizar una operación bloqueante (lectura de archivo, trabajo con BD), no marques la función como suspend ni la hagas