public static IAsyncObservable <TResult> When <TResult>(IEnumerable <AsyncPlan <TResult> > plans) { if (plans == null) { throw new ArgumentNullException(nameof(plans)); } return(Create <TResult>(async observer => { var externalSubscriptions = new Dictionary <object, IAsyncJoinObserver>(); var gate = new AsyncLock(); var activePlans = new List <ActiveAsyncPlan>(); var outputObserver = AsyncObserver.Create <TResult>( observer.OnNextAsync, async ex => { foreach (var subscription in externalSubscriptions.Values) { await subscription.DisposeAsync().ConfigureAwait(false); } await observer.OnErrorAsync(ex).ConfigureAwait(false); }, observer.OnCompletedAsync ); try { foreach (var plan in plans) { var activatedPlan = plan.Activate(externalSubscriptions, outputObserver, async activePlan => { activePlans.Remove(activePlan); if (activePlans.Count == 0) { await outputObserver.OnCompletedAsync().ConfigureAwait(false); } }); activePlans.Add(activatedPlan); } } catch (Exception ex) { return await Throw <TResult>(ex).SubscribeAsync(observer).ConfigureAwait(false); } var d = new CompositeAsyncDisposable(); foreach (var joinObserver in externalSubscriptions.Values) { await joinObserver.SubscribeAsync(gate).ConfigureAwait(false); await d.AddAsync(joinObserver).ConfigureAwait(false); } return d; })); }
private static async Task ForEachAsyncCore <TSource>(IAsyncObservable <TSource> source, Func <TSource, int, Task> onNext, CancellationToken token) { token.ThrowIfCancellationRequested(); var tcs = new TaskCompletionSource <object>(); var subscription = new SingleAssignmentAsyncDisposable(); using (token.Register(() => { tcs.TrySetCanceled(token); subscription.DisposeAsync().AsTask().ContinueWith(t => { if (t.Exception != null) { // TODO: Trace? } }); })) { var i = 0; var o = AsyncObserver.Create <TSource>( async x => { try { await onNext(x, checked (i++)).ConfigureAwait(false); } catch (Exception ex) { try { tcs.TrySetException(ex); } finally { await subscription.DisposeAsync().ConfigureAwait(false); } } }, async ex => { try { tcs.TrySetException(ex); } finally { await subscription.DisposeAsync().ConfigureAwait(false); } }, async() => { try { tcs.TrySetResult(null); } finally { await subscription.DisposeAsync().ConfigureAwait(false); } } ); // // NB: If any of the lines below throw, the result will go into the Task returned from the async method. // There's also no need to use SubscribeSafeAsync here; the exception will propagate just fine. // var d = await source.SubscribeAsync(o).ConfigureAwait(false); await subscription.AssignAsync(d).ConfigureAwait(false); } await tcs.Task.ConfigureAwait(false); }