await not awaiting при использовании в выражении LINQ внутри List <>. Вызов метода ForEach - PullRequest
1 голос
/ 08 мая 2020

У меня есть функция asyn c, в которой я должен выполнить вызов asyn c для каждого из элементов списка. Для этого я написал этот фрагмент кода:

List<string> batchItems;

batchItems.ForEach(async t => await SubmitBatchItemAsync(input, t));

Однако это не работает: SubmitBatchItemAsyn c вызывается, но его не ждут.

У меня было чтобы изменить этот код на этот, чтобы он работал:

List<string> batchItems;
foreach (var batchItem in batchItems)
    {
        await SubmitBatchItemAsync(input, batchItem);
    }

Это тоже работает, но не совсем то же самое, поскольку Task.Wait () работает не так, как await:

List<string> batchItems;

batchItems.ForEach(t => SubmitBatchItemAsync(input, t).Wait(CancellationToken.None));

Кто-нибудь знает, почему первый вариант не работает, поскольку поддержка выражений LINQ ожидает? (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#async -лямбды )

Ответы [ 2 ]

5 голосов
/ 08 мая 2020

ForEach не знает async. Требуется только Action для выполнения для каждого элемента перечисляемого.

Таким образом, это означает, что ваше лямбда-выражение async t => await SubmitBatchItemAsync(input, t) не преобразуется в Func<T, Task>, как обычно быть; он преобразуется в Action<T>, заставляя его быть async void.

И одна из ловушек async void методов заключается в том, что вызывающий код не может легко определить, когда они завершено.

TL; DR: Есть некоторые API, которые не осведомлены о async, и их следует просто избегать при работе с асинхронным кодом. ForEach - один из этих API.

1 голос
/ 08 мая 2020

Этот метод расширения может оказаться полезным для List s:

public static async Task ForEachAsync<T>(this List<T> source, Func<T, Task> function)
{
    foreach (var item in source)
    {
        await function(item);
    }
}

Пример использования:

await batchItems.ForEachAsync(async t => await SubmitBatchItemAsync(input, t));

TBH это не так полезно, как кажется, потому что при работе с с асинхронными операциями вы обычно хотите запускать их с некоторым уровнем параллелизма, а не с тем, который наступает после завершения другого.

...