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