AsyncCollections.Linq 0.2.0-alpha

This is a prerelease version of AsyncCollections.Linq.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package AsyncCollections.Linq --version 0.2.0-alpha
                    
NuGet\Install-Package AsyncCollections.Linq -Version 0.2.0-alpha
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AsyncCollections.Linq" Version="0.2.0-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AsyncCollections.Linq" Version="0.2.0-alpha" />
                    
Directory.Packages.props
<PackageReference Include="AsyncCollections.Linq" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AsyncCollections.Linq --version 0.2.0-alpha
                    
#r "nuget: AsyncCollections.Linq, 0.2.0-alpha"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AsyncCollections.Linq@0.2.0-alpha
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AsyncCollections.Linq&version=0.2.0-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=AsyncCollections.Linq&version=0.2.0-alpha&prerelease
                    
Install as a Cake Tool

AsyncCollections.Linq

Hey look! It's async LINQ operators for IAsyncEnumerable !

var ids = new int[] { 1, 2, 3 /* etc */ };

var result = await ids
    .ToAsyncEnumerable()
    .Where(x => x >= 100)
    .AsyncSelect(async x => await ProcessItem(x))
    .Take(20)
    .ToListAsync();

Download

The latest version of AsyncCollections.Linq can be downloaded on NuGet here.

Purpose

Microsoft has made it clear that they have no intentions of shipping LINQ extensions for IAsyncEnumerable and they don't think all useful functionality must be included in the standard library. Well, challenge accepted! This project aims to be a high-quality implementation of LINQ for IAsyncEnumerable.

Why not use System.Reactive.Linq ?

The great RX.NET project maintains the System.Linq.Async namespace, which contains LINQ operators for IAsyncEnumerable. Why create a new project that aims to do the same thing?

RX.NET always assumes an IAsyncEnumerable represents a linear sequence of elements with a long time delay between each element. This is fine for something like a database query (or a reactive stream), but this wasn't my use case.

Instead, I was trying to find an abstraction that could represent a collection of tasks all executing simultaneously, not just one at a time. I realized that IAsyncEnumerable was actually the perfect interface for this, but it would require LINQ operators that could compose correctly under concurrent and parallel execution.

RX.NET does not do this, so a new library was born. Keep reading for details!

Async pipelines?

I'm sure most people reading this have run into the following situation: you have a list of things and want to apply a function to each element. Normally, you would call .Select() and be on your way, but this time is tricky because your function return a Task<T>. You could write a foreach loop and await the function inside, but that's going to take much longer than if you start all the tasks and then await all of them. Usually you end up with something like this:

var ids = new int[] { 1, 2, 3, 4 };
var tasks = ids.Select(async x => await ProcessItem(x));
var results = await Task.WhenAll(tasks);

Ok, that's not so bad. But what if you want to filter the results afterwards, but your filtering function also returns a Task<bool>:

var ids = new int[] { 1, 2, 3, 4 };
var tasks = ids.Select(async x => await ProcessItem(x));
var results = await Task.WhenAll(tasks);
var tasks2 = results.Select(async x => new { IsValid = await FilterItem(x), Item = x });
var temp = await Task.WhenAll(tasks2);
var results2 = temp.Where(x => x.IsValid).Select(x => x.Item).ToList();

Oh dear, that's getting pretty terrible. If we need more operations this will turn into an absolute mess. This also isn't a very good solution, because all of our first tasks have to finish before any of the next tasks start, which means we're wasting time when we could be getting the results faster.

Surely there's a better way to build async pipelines like this?

Async pipelines!

Instead, you can use this library to write the code above as follows:

var results = await new[] { 1, 2, 3, 4 }
    .ToAsyncEnumerable()
    .AsConcurrent(preserveOrder: false)
    .AsyncSelect(async x => await ProcessItem(x))
    .AsyncWhere(async x => await FilterItem(x))
    .ToListAsync();

Now, that's much better! It's easier to read, easier to modify, extremely clear, and it's faster and will yield results sooner.

By calling .AsConcurrent() we're instructing AsyncSelect and AsyncWhere to run their selector functions concurrently, so all the tasks are all started before awaiting just like when using await Task.WhenAll() above.

The argument preserveOrder: false also tells the pipeline that results don't have to be returned in the same order as the original sequence, meaning as the tasks that finish they will immediately move to the next step. Without this, the tasks will still execute concurrently but the results will always be returned in the original order, which could decrease throughput.

Finally we apply our transformations using the async versions of the usual LINQ operators that return tasks, collect our results in a list, and we're done!

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated