without whenall method httpresponsemessage explained ejemplo create await async c# async-await

whenall - task httpresponsemessage c#



¿Cuál es la forma más elegante de cargar múltiples variables de forma asíncrona en paralelo en c# (2)

Esto se ilustra mejor con un ejemplo.

Supongamos que quiero cargar varios registros de una base de datos al comienzo de una solicitud web. Quiero obtener todos los datos necesarios de forma asincrónica. Podría tener algo como esto:

var user = await Database.GetUser(userId); var group = await Database.GetGroup(groupId); var members = await Database.GetGroupMembers(groupId);

Ahora digamos que quiero cargar estos datos en paralelo. De repente, este bonito código asíncrono claro / limpio se vuelve un poco complicado.

var userTask = Database.GetUser(userId); var groupTask = Database.GetGroup(groupId); var membersTask = Database.GetGroupMembers(groupId); await Task.WhenAll(userTask, groupTask, membersTask); var user = userTask.Result; var group = groupTask.Result; var members = membersTask.Result;

¿Hay alguna manera más sencilla y concisa de lograr esto?


En mi opinión, el código se ve bien, excepto que debería usar await lugar de Result (para una mejor semántica de manejo de errores).

var userTask = Database.GetUser(userId); var groupTask = Database.GetGroup(groupId); var membersTask = Database.GetGroupMembers(groupId); await Task.WhenAll(userTask, groupTask, membersTask); var user = await userTask; var group = await groupTask; var members = await membersTask;

Sin embargo, puede hacerse un poco mejor ahora que las tuplas están fuera. Esta semana he estado jugando con un WhenAll basado en WhenAll . Definirlo es un dolor en este momento (un enfoque basado en la reflexión sería más dinámico):

public static class AsyncTupleHelpers { public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> task1, Task<T2> task2) => (await task1.ConfigureAwait(false), await task2.ConfigureAwait(false)); public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(Task<T1> task1, Task<T2> task2, Task<T3> task3) => (await task1.ConfigureAwait(false), await task2.ConfigureAwait(false), await task3.ConfigureAwait(false)); /* More if you want, following the same pattern */ }

Pero una vez definido, puede usarse como tal:

var (user, group, members) = await AsyncTupleHelpers.WhenAll( Database.GetUser(userId), Database.GetGroup(groupId), Database.GetGroupMembers(groupId));

No se necesitan tareas separadas.

Actualización :

No lo mencioné en el ejemplo original, pero presento una excepción en caso de que no se encuentre un registro.

En ese caso, puede usar métodos locales, como tales:

async Task<User> GetUser(T userId) => await Database.GetUser(userId) ?? throw new Exception("User not found."); async Task<Group> GetGroup(T groupId) => await Database.GetGroup(groupId) ?? throw new Exception("Group not found."); var (user, group, members) = await AsyncTupleHelpers.WhenAll( GetUser(userId), GetGroup(groupId), Database.GetGroupMembers(groupId));


Separe el inicio de la tarea y await :

var userTask = Database.GetUser(userId); var groupTask = Database.GetGroup(groupId); var membersTask = Database.GetGroupMembers(groupId); var user = await userTask; var group = await groupTask; var members = await membersTask;