/// <summary> /// Merges elements from all inner async-enumerable sequences concurrently into a single async-enumerable sequence. /// All thrown Exception are thrown at the end of the enumeration of all streams. Multiple Exceptions are thrown as AggregateException. /// </summary> /// <typeparam name="T">The type of the elements in the source sequences.</typeparam> /// <param name="sources">Observable sequence of inner async-enumerable sequences.</param> /// <param name="cancellationToken">Cancellation Token to cancel the enumeration</param> /// <returns>The async-enumerable sequence that merges the elements of the inner sequences.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="sources" /> is null.</exception> public static IAsyncEnumerable <T> MergeConcurrently <T>(this IAsyncEnumerable <IAsyncEnumerable <T> > sources, CancellationToken cancellationToken = default) { var queue = new UnicastAsyncEnumerable <T>(); var tasks = new List <Task>(); Exception thrownException = null; StreamSources(); return(queue); async void StreamSources() { try { await foreach (var stream in sources.WithCancellation(cancellationToken)) { tasks.Add(StreamSource(stream)); } // After the last stream arrived, we have to wait until all of those streams have completely finished with streaming await Task.WhenAll(tasks); if (thrownException != null) { await queue.Error(thrownException); } } catch (OperationCanceledException) { } catch (Exception e) { ExceptionHelper.AddException(ref thrownException, e); await queue.Error(thrownException); } finally { await queue.Complete(); } } async Task StreamSource(IAsyncEnumerable <T> source) { try { await foreach (var item in source.WithCancellation(cancellationToken)) { await queue.Next(item); } } catch (OperationCanceledException) { } catch (Exception e) { ExceptionHelper.AddException(ref thrownException, e);; } } }
/// <summary> /// Merges elements from all inner async-enumerable sequences concurrently into a single async-enumerable sequence. /// Enumeration stops on first thrown Exception of any inner streams. /// </summary> /// <typeparam name="T">The type of the elements in the source sequences.</typeparam> /// <param name="sources">Observable sequence of inner async-enumerable sequences.</param> /// <returns>The async-enumerable sequence that merges the elements of the inner sequences.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="sources" /> is null.</exception> public static IAsyncEnumerable <T> MergeConcurrentlyUntilFirstException <T>(this IAsyncEnumerable <IAsyncEnumerable <T> > sources) { var queue = new UnicastAsyncEnumerable <T>(); var tasks = new List <Task>(); StreamSources(); return(queue); async void StreamSources() { try { await foreach (var stream in sources) { tasks.Add(StreamSource(stream)); } // After the last stream arrived, we have to wait until all of those streams have completely finished with streaming await Task.WhenAll(tasks); } catch (OperationCanceledException) { } catch (Exception e) { await queue.Error(e); } finally { await queue.Complete(); } } async Task StreamSource(IAsyncEnumerable <T> source) { try { await foreach (var item in source) { await queue.Next(item); } } catch (OperationCanceledException) { } catch (Exception e) { await queue.Error(e); } } }
public async void Call_After_Done_2() { var push = new UnicastAsyncEnumerable <int>(); await push.Error(new InvalidOperationException()); await push.Complete(); await push.Next(1); await push.Error(new IndexOutOfRangeException()); await push.AssertFailure(typeof(InvalidOperationException)); }
public static async IAsyncEnumerable <T> If <T>(this IAsyncEnumerable <T> source, Func <T, bool> ifSelector, Func <IAsyncEnumerable <T>, IAsyncEnumerable <T> > ifFunction, Func <IAsyncEnumerable <T>, IAsyncEnumerable <T> > elseFunction = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var ifSourceStream = new UnicastAsyncEnumerable <T>(); var elseSourceStream = new UnicastAsyncEnumerable <T>(); var ifResultStream = ifFunction(ifSourceStream); var elseResultStream = elseFunction?.Invoke(elseSourceStream) ?? elseSourceStream; HandleStream(); var result = ifResultStream.MergeConcurrently(elseResultStream); try { await foreach (var item in result.WithCancellation(cancellationToken)) { yield return(item); } } finally { await ifSourceStream.Complete(); await elseSourceStream.Complete(); } async void HandleStream() { try { await foreach (var item in source.WithCancellation(cancellationToken)) { if (ifSelector(item)) { await ifSourceStream.Next(item); } else { await elseSourceStream.Next(item); } } } catch (OperationCanceledException) { } catch (Exception e) { await elseSourceStream.Error(e); } finally { await ifSourceStream.Complete(); await elseSourceStream.Complete(); } } }
public async void Offline_Error() { var push = new UnicastAsyncEnumerable <int>(); for (var i = 1; i <= 5; i++) { await push.Next(i); } await push.Error(new InvalidOperationException()); await push.AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); }
public async void Error_After_Dispose() { var push = new UnicastAsyncEnumerable <int>(); var t = push.Take(1).AssertResult(1); await push.Next(1); await t; await push.Error(new InvalidOperationException()); }
public async void Call_After_Done() { var push = new UnicastAsyncEnumerable <int>(); await push.Complete(); await push.Error(new InvalidOperationException()); await push.Next(1); await push.Complete(); await push.AssertResult(); }