LINQで非同期ラムダを待機する

System.Linq.Asyncのバージョン4.0.0で、LINQの各メソッドに非同期ラムダを受けて待機するバリエーションである~Awaitシリーズが追加された。以前までは自分で用意する必要があって微妙でIssueもいくつか立っていたが、パッケージに含まれるようになったので楽!

例)

var array = new[] { 1, 2, 3 };
var tasks = array.Select((x) => GetAsync(x));
// Task.WhenAll(tasks) ...

みたいな書き方をすると、非同期ラムダ式の戻り型はTaskなので、複数のTaskが生成されてGetAsyncが全て並列に動く(正確にはawaitのタイミングでTaskが返って次が走り始める)。これを、1つずつ直列にただし非同期で動かしたいとする。

var array = new[] { 1, 2, 3 };
var tasks = array.Select((x) => GetAsync(x));
foreach (var task in tasks)
{
    var value = await task;
    // ...
}

これだけならTaskを1つずつawaitすれば事足りる。しかしSelectの後段でLINQメソッドをつなげたい場合はIAsyncEnumerableで扱うと効果的。SelectではなくSelectAwaitを使える。

var array = new[] { 1, 2, 3 };
var values = array
    .ToAsyncEnumerable()
    .SelectAwait(async (x) => await GetAsync(x))
    .ToArray();

以前の自前実装したものとSelectAwaitを使ったものと、両方こちらの回答に書いた。

stackoverflow.com

SelectAwaitに限らず、ForEachAwaitAsyncなどIAsyncEnumerableの各種LINQメソッドに~Awaitシリーズが追加されているので是非活用してほしい。