Async streams y flujos de valores
Cuando algo emite valores a lo largo del tiempo —ubicaciones, mensajes, progreso— los async streams te dejan recorrerlos con un bucle for-await, como si fueran una colección.
async/await es perfecto para “pide una cosa, espera una respuesta”. Pero muchas fuentes emiten valores continuamente: la ubicación del GPS, mensajes que llegan, el progreso de una descarga. Para eso están los async streams: una secuencia de valores en el tiempo que recorres con un bucle.
- Qué es un
AsyncSequence. - Recorrer valores con
for await. - Crear tus propios flujos con
AsyncStream. - Cancelación y ciclo de vida.
Un bucle sobre el tiempo
Un AsyncSequence es como un array cuyos elementos van llegando poco a poco. Lo recorres con for await, que espera cada valor:
for await ubicacion in gestorUbicacion.actualizaciones {
print("Nueva posición: \(ubicacion)")
// el bucle continúa cada vez que llega una ubicación nueva
}
El bucle no termina cuando “se acaban los datos ahora”: se pausa esperando el siguiente valor, y sigue cuando llega. Ideal para fuentes vivas.
Crear un flujo con AsyncStream
Envuelve cualquier fuente de eventos (un temporizador, un callback antiguo, un sensor) en un AsyncStream:
func contador() -> AsyncStream<Int> {
AsyncStream { continuation in
Task {
for i in 1...5 {
try? await Task.sleep(for: .seconds(1))
continuation.yield(i) // emite un valor
}
continuation.finish() // termina el flujo
}
}
}
// consumirlo:
for await n in contador() {
print(n) // 1, 2, 3, 4, 5, uno por segundo
}
yield emite; finish cierra. Cualquier API basada en callbacks o notificaciones puede volverse un stream limpio y recorrible.
AsyncStream es tu herramienta para modernizar código: envuelves una API antigua basada en delegados o closures repetidos, y por fuera ofreces un for await elegante. En vez de esparcir lógica en métodos de callback desconectados, la concentras en un bucle legible. Muchas de las APIs del sistema (ubicación, notificaciones, NotificationCenter) ya ofrecen versiones async así. Reconocer cuándo algo “emite valores en el tiempo” y modelarlo como stream es una señal de madurez con la concurrencia.
Cancelación
Los streams respetan la cancelación de tareas: si la Task que los consume se cancela (por ejemplo, la vista desaparece y su .task se cancela), el bucle for await termina de forma limpia. Por eso .task en SwiftUI y los streams se llevan tan bien:
.task {
for await evento in fuente.eventos {
procesar(evento)
}
// si la vista desaparece, el bucle se corta solo
}
En trabajo largo dentro de una tarea, comprueba Task.isCancelled o llama a try Task.checkCancellation() de vez en cuando para salir pronto si ya no hace falta el resultado. Respetar la cancelación mantiene tu app eficiente: no gastes batería y datos en algo que el usuario ya dejó atrás.
- Escribe una función que devuelva un
AsyncStream<Int>emitiendo números cada segundo. - Consúmelo con
for awaite imprime cada valor. - Conéctalo a una vista con
.taskpara actualizar un@Stateen tiempo real. - Comprueba que, al salir de la vista, el flujo se detiene solo.