En C#, async y await son palabras claves que simplifican la sintaxis al trabajar con tareas asíncronas, facilitando además su lectura.
- async: Se coloca antes de un método para indicar que contiene operaciones asíncronas.
- await: Pausa la ejecución hasta que se complete la tarea asíncrona y permite liberar el hilo para que ejecute otras tareas mientras tanto.
¿Cómo funcionan async y await?
Cuando un método se marca como async
, significa que este método no bloquea al hilo que lo ejecuta (es decir, se ejecuta asíncronamente).
Por otro lado, un método async
puede contener una o más operaciones marcadas con await
. Esto significa que el método va a “esperar” que esta operación termine, antes de continuar.
Una vez que la operación “awaiteada” se completa, el método async
retoma la ejecución en el punto donde quedó.
Es importante recordar que:
- Los métodos
async
deben devolverTask
,Task<T>
ovoid
. await
solo se puede utilizar dentro de métodosasync
.
Es un error típico pensar que hay que awaitear todos los métodos async. Es perfectamente válido querer lanzar un método async, y que este se ejecute en paralelo.
Ejemplo de async y await
Vamos a verlo con un ejemplo. Supongamos que tenemos una OperacionLarguisima
que queremos ejecutar de forma no bloqueante.
En primer lugar, vamos a marcar el método como asincrónico utilizando la palabra clave async
.
// Método asíncrono que simula una operación de larga duración
static async Task OperacionLarguisima()
{
Console.WriteLine("Inicio de operación de larga duración...");
await Task.Delay(3000); // Simulamos un proceso de 3
// Resto del código
Console.WriteLine("Operación completada.");
}
Eso significa que cuando invoquemos esa función, se va a ejecutar de forma no bloqueante (en paralelo).
Ahora supongamos que queremos llamar a esa función. Podríamos hacerlo así.
static async Task Main()
{
Console.WriteLine("Inicio del Main...");
// Llama a un método asíncrono
await OperacionLarguisima();
Console.WriteLine("Fin del main.");
}
Si ejecutamos esto, tendremos de resultado
Inicio del Main...
Inicio de operación de larga duración...
# (una pausa de 3s).
Operación completada.
Fin del main.
Es decir,
- La función
Main
ha empezado, y ha llamado aOperacionLarguisima
Main
ha esperado que acabeOperacionLarguisima
Main
ha finalizado
La “cadena” de asyncs
Una cosa curiosa del async y await es que “hacen cadeneta”. El método que marcas como async, tiene que ser invocado por un async, que tiene que ser invocado por…
Es decir, una vez que empiezas a usar métodos async, todo lo que lo use va a tener que ser async (hasta llegar al método Main principal). En algún momento vas a tener que “partir la cadeneta”.
Para ello, es recordad que podéis usar Task.Wait();
. Por ejemplo, esta versión del Main
anterior no es asíncrona.
static void MainSincrono()
{
Console.WriteLine("Inicio del Main...");
OperacionLarguisima.Wait();
Console.WriteLine("Fin del main.");
}
Combinación de múltiples tareas asíncronas
En algunos casos, es necesario ejecutar múltiples tareas de forma asíncrona. Esto puede hacerse usando Task.WhenAll
o Task.WhenAny
.
- Task.WhenAll: Ejecuta múltiples tareas de manera concurrente y espera hasta que todas se completen.
- Task.WhenAny: Ejecuta múltiples tareas y retorna cuando cualquiera de ellas se complete.
static async Task EjecutarTareasConcurrently()
{
Task tarea1 = Task.Delay(2000); // Simula una tarea de 2 segundos
Task tarea2 = Task.Delay(3000); // Simula una tarea de 3 segundos
// Espera hasta que ambas tareas se completen
await Task.WhenAll(tarea1, tarea2);
Console.WriteLine("Todas las tareas han finalizado.");
}