Async/Await: esencia, funciones async y trabajo con await

Autor: IT Sectr Publicado: 2026-03-16 Tiempo de lectura: 8 min

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 azúcar sintáctico para código asíncrono sin callbacks anidados
  • async marca una función como asíncrona y suspendible
  • await suspende la ejecución de la función hasta que se completa la operación asíncrona
  • Task en Swift crea una nueva unidad de trabajo asíncrona con contexto
  • Structured Concurrency garantiza que todas las tareas hijas finalicen antes que la padre

¿Qué es Async/Await y la programación asíncrona?

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.

Principio de funcionamiento: funciones suspendibles

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.

Cómo funciona Async/Await: suspensión y reanudación

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.

  • Swift — el compilador traduce la función async a SIL con un runAsync coroutine frame
  • Kotlin — cada función suspendida recibe un Continuation y delega la ejecución al dispatcher
  • Ambos lenguajes — liberan el hilo en await, sin bloquearlo durante la espera
  • Excepciones — se propagan mediante mecanismos estándar (throws / try-catch)

Async/Await vs Callbacks y GCD: comparación de enfoques

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.

AspectoCallbackGCDAsync/Await
AnidamientoProfundo (pirámide de la perdición)Medio (grupos+notify)Lineal (plano)
Manejo de erroresPor callbackManualUnificado (try/catch)
CancelaciónManualLimitadaTask.cancel()
Thread safetyManualMediante cola serialMediante 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.

Swift Concurrency: Task, TaskGroup y Actor

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.

Structured Concurrency

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.

Async/Await en Kotlin con corrutinas

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.

Ejemplos de código con Async/Await en Swift y Kotlin

Veamos dos ejemplos para cada lenguaje: carga secuencial de datos y solicitudes paralelas con TaskGroup o async/await.

Swift: Llamadas async secuenciales

await suspende la ejecución hasta recibir los datos del usuario, luego hasta recibir los amigos. Sin cierres anidados.

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

Swift: Solicitudes paralelas con TaskGroup

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.

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

Kotlin: Llamada secuencial con async/await

Las funciones suspend pueden llamar a otras funciones suspend. Análogo a Swift: llamadas secuenciales.

kotlin
suspend fun loadProfile(): Profile {
    val user = api.fetchUser()
    val friends = api.fetchFriends(user.id)
    return Profile(user, friends)
}

Kotlin: Solicitudes paralelas con async/await

En Kotlin, el paralelismo se logra mediante coroutineScope con async para cada solicitud.

kotlin
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())
}

Errores comunes y limitaciones de Async/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á.

Uso excesivo de async sin necesidad

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.

Bloqueo del hilo principal mediante .result

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.

Fuga de Task al cancelar

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

¿En qué se diferencia async/await de DispatchQueue?

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.

¿Bloquea await el hilo?

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.

¿Se puede cancelar una tarea async?

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().

¿Qué es MainActor en Swift?

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.

¿En qué lenguajes existe async/await?

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

  • Async/Await es una construcción del lenguaje para código asíncrono lineal sin callback hell
  • Swift Concurrency proporciona Task, TaskGroup, Actor y Structured Concurrency
  • Las corrutinas de Kotlin usan funciones suspend, Dispatchers y ámbitos de corrutinas
  • La suspensión sin bloqueo es la diferencia clave de async/await respecto a hilos y colas
  • Structured Concurrency garantiza que todas las tareas hijas finalicen antes que la padre
  • Async/Await vs GCD — el primero es preferible para código nuevo, el segundo para legacy y bajo nivel
  • MainActor en Swift y Dispatchers.Main en Kotlin protegen el hilo de UI de operaciones en segundo plano

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.

Discutir el proyecto

Lea también