Saltar al contenido

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 async y la palabra await.
  • Lanzar trabajo asíncrono con Task.
  • Concurrencia estructurada: async let.
  • El modificador .task en 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:

SWIFT
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:

SWIFT
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:

SWIFT
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):

SWIFT
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

  1. Escribe una función async que espere un segundo y devuelva un texto.
  2. Llámala desde un botón con Task { await … }.
  3. Lanza dos funciones async en paralelo con async let y espera ambas.
  4. Carga datos al mostrar una vista con el modificador .task.