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:

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.