Async Let: agilizando la ejecución concurrente
Async Let
emerge como una herramienta esencial para acelerar procesos en Swift. Nos permite ejecutar varias tareas simultáneamente, lo que mejora significativamente la velocidad de ejecución. A través de ejemplos prácticos, podemos comprender cómo Async Let
facilita la creación de constantes asíncronas para cada tarea, permitiendo que todas avancen al mismo tiempo y, así, optimizando el rendimiento de nuestra aplicación.
Task {
do {
async let fetchTitle = fetchTitle()
async let fetchImage1 = fetchImage()
async let fetchImage2 = fetchImage()
async let fetchImage3 = fetchImage()
async let fetchImage4 = fetchImage()
// En este punto esperamos por todas las llamadas,
// por lo que se ejecutarán en segundo plano
// y se tardará lo que tarden todas en finalizar
let (title, image1, image2, image3, image4) = await (fetchTitle, try fetchImage1, try fetchImage2, try fetchImage3, try fetchImage4)
await MainActor.run {
self.images.append(contentsOf: [image1, image2, image3, image4])
self.title = title
}
} catch {
print(error)
}
}
Varias cuestiones a tener en cuenta:
- Las llamadas
async let
que se hagan dentro de unaTask
heredarán la prioridad de laTask
padre - Poseemos llamadas asíncronas definidas, lo que nos supone ahorro de código. Se emplea la keyword
await
solo una vez, en aquel momento en el que queremos recibir los datos. - Perfecto para hacer 5 o 6 llamadas que necesitamos realizar antes de cargar una pantalla: título, posts, ajustes del usuario, etc.
- Como contra: se puede acumular mucho código, no es muy escalable, etc.
Diferencia clave: Await vs Async Let
Una distinción importante radica en la diferencia entre el uso del await
y Async Let
. Mientras await
nos obliga a esperar explícitamente la respuesta de una tarea, Async Let
, como ya hemos enunciado, nos permite crear constantes asíncronas sin esperar de manera explícita. Esta flexibilidad nos permite gestionar tareas concurrentes de manera más eficiente y conseguir mejor performance.
Escalabilidad y limitaciones: el rol de Task Group
A pesar de las ventajas de Async Let
, es importante reconocer sus limitaciones, especialmente en entornos con un gran volumen de solicitudes. Es aquí donde entra en juego TaskGroup
, una herramienta que permite la ejecución concurrente de múltiples tareas de manera escalable y eficiente. TaskGroup
nos proporciona una forma robusta de gestionar una gran cantidad de solicitudes, garantizando un rendimiento óptimo en nuestras aplicaciones Swift.
func fetchImagesWithTaskGroup() async throws -> [UIImage] {
let urlStrings: [String] = [
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
"https://picsum.photos/300",
]
return try await withThrowingTaskGroup(of: UIImage?.self) { group in
var images: [UIImage] = []
// Para optimizar nuestro código,
// es muy usado en este contexto reserveCapacity
images.reserveCapacity(urlStrings.count)
for url in urlStrings {
group.addTask {
// Muy importante usar el try? ya que si
// una foto falla, no se enviará ninguna
// debido a que lanzará el throw
try? await self.fetchImage(urlString: url)
}
}
for try await image in group {
// Aquí comprobar si es opcional o no.
// Con esta técnica nos aseguramos de
// que se envíen todas las fotos posibles
// y que aquellas que fallen, se reciba nil.
// Por eso es importante devolver opcional UIImage?
if let image = image {
images.append(image)
}
}
return images
}
}
Reflexión final
Al aprovechar las capacidades de Async Let
y TaskGroup
en Swift, abrimos un abanico de posibilidades para mejorar la eficiencia y el rendimiento de nuestras aplicaciones. Estas herramientas nos permiten gestionar la concurrencia de manera efectiva, lo que se traduce en una experiencia de usuario más fluida y un rendimiento excepcional en nuestras aplicaciones. Al incorporar estas técnicas en nuestro arsenal de desarrollo, podemos elevar nuestras habilidades y ofrecer aplicaciones Swift de primera categoría.