async/await y tareas
Escribe código asíncrono que se lee como síncrono. async, await, Task y la concurrencia estructurada que hace legible lo que antes eran callbacks anidados.
Descargar datos, leer un archivo, esperar una animación: todo eso lleva tiempo, y bloquear la app mientras ocurre es inaceptable. async/await te deja escribir ese código asíncrono como si fuera una secuencia normal, sin la “pirámide de la perdición” de los callbacks.
- Funciones
asyncy la palabraawait. - Lanzar trabajo asíncrono con
Task. - Concurrencia estructurada:
async let. - El modificador
.tasken SwiftUI.
async y await
Una función que tarda se marca async. Al llamarla, pones await: ahí tu código “se pausa” hasta que el resultado esté listo, sin bloquear el hilo:
func obtenerUsuario() async -> String { // simula una espera (una red, un disco…) try? await Task.sleep(for: .seconds(1)) return "William"}// llamarla:let nombre = await obtenerUsuario()print("Hola, \(nombre)")El código tras el await se ejecuta cuando el resultado llega, pero se lee de arriba abajo como algo secuencial. Ahí está la magia.
Adiós a la pirámide de callbacks
Antes, encadenar tres operaciones asíncronas era un anidamiento infernal: un callback dentro de otro dentro de otro, con el manejo de errores desperdigado. Con async/await, esas tres operaciones son tres líneas seguidas con await, y los errores se manejan con el do/try/catch normal (Nivel 1). El código asíncrono por fin se lee como el síncrono: en orden, plano y con el flujo de errores unificado. Es uno de los mayores saltos de legibilidad en la historia de Swift.
Task: entrar en el mundo async
Desde código síncrono (como la acción de un botón), lanzas trabajo asíncrono con Task:
Button("Cargar") { Task { let nombre = await obtenerUsuario() print(nombre) }}Task crea una unidad de trabajo concurrente. También puedes cancelarla y comprobar cancelación, importante para no malgastar recursos.
Concurrencia estructurada: async let
Si necesitas varias cosas a la vez, async let las lanza en paralelo y esperas a ambas juntas:
func cargarPerfil() async -> (String, Int) { async let nombre = obtenerNombre() // arrancan async let edad = obtenerEdad() // ambas a la vez return (await nombre, await edad) // esperas los dos resultados}Sin async let, harías await de una y luego de otra: en serie, más lento. Con él, corren en paralelo y el total es el tiempo de la más lenta, no la suma.
.task en SwiftUI
Para cargar datos cuando aparece una vista, el modificador .task es perfecto: lanza trabajo async atado al ciclo de vida de la vista (se cancela solo si la vista desaparece):
struct PerfilView: View { @State private var nombre = "Cargando…" var body: some View { Text(nombre) .task { nombre = await obtenerUsuario() } }}.task, no .onAppear + Task
.task es la forma idiomática de cargar datos al mostrar una vista: es async de forma nativa y cancela el trabajo automáticamente si el usuario navega fuera antes de que termine. Evita el viejo patrón de .onAppear { Task { … } }, que no gestiona la cancelación. Detalle pequeño, gran diferencia en apps que hacen muchas peticiones.
Piensa en asíncrono
- Escribe una función
asyncque espere un segundo y devuelva un texto. - Llámala desde un botón con
Task { await … }. - Lanza dos funciones async en paralelo con
async lety espera ambas. - Carga datos al mostrar una vista con el modificador
.task.