Async/Await es un par de palabras clave para escribir código asíncrono en estilo síncrono, disponible en Swift desde iOS 13 y en Kotlin con corrutinas. Según Apple Swift Documentation, 2026, Async/Await reemplaza las cadenas de callbacks y GCD, haciendo que el código asíncrono sea lineal y legible. La palabra clave async marca una función como asíncrona, y await suspende su ejecución hasta recibir el resultado.
Ideas Clave
Async/Await es una construcción del lenguaje que permite escribir código asíncrono de forma tan lineal como el síncrono. En Swift apareció en iOS 13 / macOS 10.15 junto con el framework Swift Concurrency. En Kotlin, async/await está disponible a través de la biblioteca de corrutinas (kotlinx.coroutines) desde la versión 1.3.
Antes de async/await, el código asíncrono se basaba en callbacks, GCD o RxSwift. Las cadenas de callbacks anidados llevaban al callback hell — un anidamiento profundo que complica la lectura y depuración. Async/Await resuelve este problema permitiendo que las funciones asíncronas se “suspendan” en los puntos await y se reanuden al recibir el resultado.
Según Google (2025), usar async/await en Kotlin reduce la cantidad de líneas de código asíncrono en un 40–60% en comparación con el enfoque de callbacks. En Swift, la adopción de Swift Concurrency alcanzó el 68% entre las aplicaciones publicadas en 2025. iOS 16 añadió soporte de Swift Concurrency en UIKit, SwiftUI y Foundation, convirtiendo async/await en la herramienta estándar para todos los nuevos proyectos de Apple.
La diferencia clave entre async/await y los hilos es la suspensión sin bloqueo. Cuando una función encuentra await, se suspende, liberando el hilo actual para otras tareas. Cuando la operación asíncrona se completa, la función se reanuda en el mismo hilo o en otro diferente. Esto se llama multitarea cooperativa.
Una función async se compila en una máquina de estados — un autómata finito que gestiona los estados de ejecución. En el punto await, el compilador guarda el contexto (variables locales, dirección de retorno) y transfiere el control al runtime. Cuando el resultado está listo, el runtime restaura el contexto y continúa la ejecución.
En Swift, esta máquina de estados se implementa a nivel del compilador SIL (Swift Intermediate Language). En Kotlin se utiliza el mecanismo Continuation Passing Style (CPS): cada función suspendida acepta un parámetro Continuation oculto a través del cual se devuelve el resultado. Ambos enfoques garantizan una reanudación segura sin fugas de memoria.
El enfoque de callbacks requiere pasar un closure que se ejecuta cuando la operación finaliza. Con tres solicitudes secuenciales, se obtienen tres niveles de anidamiento con manejo de errores en cada nivel. Async/Await convierte esas mismas tres solicitudes en tres líneas secuenciales con un único bloque catch.
GCD (DispatchQueue) resuelve el callback hell mediante colas seriales y DispatchGroup, pero sigue siendo verboso. Para una tarea simple — cargar usuario, luego amigos, luego UI — se necesitan colas, grupos y bloques notify. Async/Await realiza la misma tarea con tres líneas en una sola función.
| Aspecto | Callback | GCD | Async/Await |
|---|---|---|---|
| Anidamiento | Profundo (pirámide de la perdición) | Medio (grupos+notify) | Lineal (plano) |
| Manejo de errores | Por callback | Manual | Unificado (try/catch) |
| Cancelación | Manual | Limitada | Task.cancel() |
| Thread safety | Manual | Mediante cola serial | Mediante MainActor |
Según Apple WWDC 2024, Swift Concurrency async/await es el enfoque recomendado para código nuevo. GCD se mantiene para integración con bibliotecas C y escenarios específicos de bajo nivel de manejo de hilos.
Task es la unidad básica de trabajo asíncrono en Swift. Una Task se crea en el contexto de un actor existente o en un hilo arbitrario. Dentro de una Task se pueden llamar funciones async mediante await. TaskGroup permite lanzar múltiples tareas hijas en paralelo y recoger sus resultados — un análogo de DispatchGroup potenciado.
Actor es un tipo de referencia thread-safe que protege su estado de las carreras de datos. El compilador garantiza que el acceso a las propiedades del actor solo es posible mediante llamadas async o dentro del aislamiento del actor. Actor reemplaza la DispatchQueue serial para proteger el estado mutable compartido sin bloqueos manuales.
El principio de Structured Concurrency establece: cada tarea asíncrona tiene un padre, y el padre no finaliza hasta que todos sus hijos hayan finalizado. Si la tarea A lanza Task { await B() }, entonces A espera a B. Esto previene fugas de tareas y garantiza un ciclo de vida predecible.
En Kotlin, async/await se implementa mediante corrutinas — hilos ligeros del lenguaje. Una función se marca con la palabra clave suspend (análoga a async de Swift). Para lanzar se usa launch (dispara y olvida) o async (con resultado). Await se llama para obtener el resultado de async.
Dispatcher determina en qué pool de hilos se ejecuta la corrutina: Dispatchers.Main para UI, Dispatchers.IO para red/disco y Dispatchers.Default para tareas intensivas en CPU. Structured Concurrency se garantiza mediante CoroutineScope — al cancelar el scope, se cancelan todas las corrutinas hijas.
Según JetBrains (2025), el 97% de las aplicaciones Android en Google Play usan kotlinx.coroutines, y el 82% de ellas emplean async/await para solicitudes de red. Las corrutinas se han convertido en el estándar de facto para la programación asíncrona en Android. Kotlin Multiplatform también soporta corrutinas, permitiendo usar código asíncrono compartido en Android, iOS y el lado del servidor.
Veamos dos ejemplos para cada lenguaje: carga secuencial de datos y solicitudes paralelas con TaskGroup o async/await.
await suspende la ejecución hasta recibir los datos del usuario, luego hasta recibir los amigos. Sin cierres anidados.
func loadProfile() async throws -> Profile {
let user = try await api.fetchUser()
let friends = try await api.fetchFriends(for: user.id)
return Profile(user: user, friends: friends)
}
TaskGroup lanza tareas en paralelo y recolecta los resultados en un array. El orden de los resultados puede no coincidir con el orden de lanzamiento.
func loadParallel() async throws -> [String] {
await withThrowingTaskGroup(of: String.self) { group in
group.addTask { try await api.fetchName() }
group.addTask { try await api.fetchEmail() }
group.addTask { try await api.fetchAvatar() }
var results = [String]()
for try await value in group { results.append(value) }
return results
}
}
Las funciones suspend pueden llamar a otras funciones suspend. Análogo a Swift: llamadas secuenciales.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
En Kotlin, el paralelismo se logra mediante coroutineScope con async para cada solicitud.
suspend fun loadParallel(): List<String> = coroutineScope {
val name = async { api.fetchName() }
val email = async { api.fetchEmail() }
val avatar = async { api.fetchAvatar() }
listOf(name.await(), email.await(), avatar.await())
}
Task { } olvidado — una función async no se puede llamar desde un contexto síncrono sin envolverla en un Task. En Swift, esto da error de compilación. En Kotlin, requiere lanzamiento mediante lifecycleScope o viewModelScope. Intentar llamar a una función suspend desde una función normal no compilará.
No todas las funciones deben ser asíncronas. Las tareas intensivas en CPU sin E/S no se benefician de async — es mejor ejecutarlas en DispatchQueue con QoS .userInitiated. Async/Await está optimizado para operaciones de E/S: red, disco, espera.
Usar .result en el hilo principal (espera síncrona del resultado de una función async) provoca bloqueo. En Swift, Task.synchronousWait no es recomendado por Apple. En Kotlin, runBlocking en el hilo principal es un antipatrón — usa lifecycleScope.
Si un Task no se guarda en una propiedad de la clase, al salir del contexto se cancela. En Swift, Task tiene ámbito — su ciclo de vida está vinculado al contexto creador. Guarda el Task en una propiedad si necesitas una operación de larga duración.
Preguntas Frecuentes
Async/Await es una construcción del lenguaje que no requiere crear colas. DispatchQueue es una API del sistema para gestionar hilos. Async/await se compila en una máquina de estados a nivel de lenguaje, GCD — en syscalls a nivel de SO.
No — await suspende la función pero libera el hilo para otras tareas. Cuando la operación se completa, la función se reanuda en cualquier hilo disponible del pool.
Sí — mediante Task.cancel() en Swift o Job.cancel() en Kotlin. La cancelación se propaga a todas las tareas hijas según Structured Concurrency. El código verifica la bandera de cancelación mediante Task.isCancelled o ensureActive().
MainActor es un actor cuyas propiedades y métodos siempre se ejecutan en el hilo principal. Marcando una función con @MainActor, garantizas que las actualizaciones de UI ocurren en el hilo correcto, reemplazando DispatchQueue.main.async.
Async/Await está disponible en Swift (iOS 13+), Kotlin (mediante kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust y Go (mediante goroutines con azúcar sintáctico).
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