Async/Await (Parte 10 de 11)
1Paralelización con async let
2Calcular tiempo de ejecución de un código
3¿Qué es un Task?
4¿Qué es un Actor y para que se utilizan?
5¿Qué es Main Actor?
6Modificador task
7Cómo utilizar Task Group
8Trabajando con iteraciones asíncronas usando AsyncSequence
9Mezclando código síncrono y asíncrono usando CheckedContinuation
10Optimiza la eficiencia de tu código concurrente con DiscardingTaskGroup
11Explorando Task-local
Optimiza la eficiencia de tu código concurrente con DiscardingTaskGroup
Libranner Santos
02 agosto, 20232min de lectura
En ocasiones, no sabemos la cantidad de procesos concurrentes que necesitaremos en tiempo de desarrollo, en eso estos utilizamos TaskGroup
. Ya hablamos inicialmente sobre estos en un artículo anterior.
Antes, cuando necesitábamos manejar procesos concurrentes sin retornar nada, teníamos que usar TaskGroup y especificar un tipo, como Void.self
. Veamos un ejemplo:
func execute() async {await withTaskGroup(of: Void.self) { group infor _ in list {group.addTask {await fetch()}}...}}
En este código recorremos un arreglo list
y creamos una tarea para ejecutar fetch()
por cada miembro del arreglo.
Pero ahora, con la nueva incorporación en WWDC 2023, contamos con la API WithDiscardingTaskGroup
, lo que nos permite simplificar el código:
func execute() async {await withDiscardingTaskGroup { group infor _ in list {group.addTask {await fetch()}}...}}
¡Genial!, ¿Verdad? Ya no tenemos que preocuparnos por especificar un tipo de retorno como Void.self
.
Beneficios de usar DiscardingTaskGroup
Usar WithDiscardingTaskGroup
trae consigo otros beneficios que nos ayudarán a escribir código más eficiente:
- No retienen los resultados: los recursos utilizados por las tareas hijas se liberan inmediatamente después de que la tarea finaliza. Esto ayuda a reducir el consumo de memoria cuando se tienen muchas tareas que no necesitan devolver nada.
- Eliminan automáticamente a sus tareas hijas: por lo que no es necesario cancelar explícitamente el grupo o hacer limpieza. Ya no necesitamos usar
group.cancelAll()
, como cuando usábamoswithThrowingTaskGroup
. - Manejo de errores: si alguna de las tareas hijas genera un error, todas las tareas restantes se cancelan automáticamente. Para cuando tengamos grupos de tareas que puedan generar un error, contamos con
withThrowingDiscardingTaskGroup
.
Convirtiendo TaskGroup en DiscardingTaskGroup
Un código como el siguiente, donde debemos:
- Ejecutar
try await group.next()
para poder ir almancenado los valores retornados. - Ejecutar
group.cancelAll()
para liberar recursos.
try? await withThrowingTaskGroup(of: Void.self) { group infor _ in list {group.addTask {await fetch()}}group.addTask {try await Task.sleep(for: shiftDuration)}try await group.next()group.cancelAll()}
Se convertirá en lo siguiente si usamos WithDiscardingTaskGroup
:
try? await withThrowingDiscardingTaskGroup { group infor _ in list {group.addTask {await fetch()}}group.addTask {try await Task.sleep(nanoseconds: 1_000)}}
Con este pequeño cambio aprovecharemos todos los beneficios que nos ofrece withThrowingDiscardingTaskGroup
.
Async/Await (Parte 10 de 11)
1Paralelización con async let
2Calcular tiempo de ejecución de un código
3¿Qué es un Task?
4¿Qué es un Actor y para que se utilizan?
5¿Qué es Main Actor?
6Modificador task
7Cómo utilizar Task Group
8Trabajando con iteraciones asíncronas usando AsyncSequence
9Mezclando código síncrono y asíncrono usando CheckedContinuation
10Optimiza la eficiencia de tu código concurrente con DiscardingTaskGroup
11Explorando Task-local