|  | @@ -15,6 +15,8 @@ using System.Threading.Tasks;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace System.Reactive.Linq
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +    // TODO: Add overloads with tuples.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      partial class AsyncObservable
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |  <#
 | 
	
	
		
			
				|  | @@ -24,7 +26,41 @@ for (var i = 2; i <= 15; i++)
 | 
	
		
			
				|  |  |      var genArgs = string.Join(", ", Enumerable.Range(1, i).Select(j => "T" + j));
 | 
	
		
			
				|  |  |      var args = string.Join(", ", Enumerable.Range(1, i).Select(j => "IAsyncObservable<T" + j + "> source" + j));
 | 
	
		
			
				|  |  |      var obs = "(" + string.Join(", ", Enumerable.Range(1, i).Select(j => "observer" + j)) + ")";
 | 
	
		
			
				|  |  | +    var tuple = "(" + genArgs + ")";
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +        public static IAsyncObservable<<#=tuple#>> Zip<<#=genArgs#>>(this <#=args#>)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +for (var j = 1; j <= i; j++)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +            if (source<#=j#> == null)
 | 
	
		
			
				|  |  | +                throw new ArgumentNullException(nameof(source<#=j#>));
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return Create<<#=tuple#>>(async observer =>
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var d = new CompositeAsyncDisposable();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                var <#=obs#> = AsyncObserver.Zip(observer);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +for (var j = 1; j <= i; j++)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +                var sub<#=j#> = source<#=j#>.SubscribeSafeAsync(observer<#=j#>).ContinueWith(disposable => d.AddAsync(disposable.Result)).Unwrap();
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  #>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                await Task.WhenAll(<#=string.Join(", ", Enumerable.Range(1, i).Select(j => "sub" + j))#>).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                return d;
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          public static IAsyncObservable<TResult> Zip<<#=genPars#>>(this <#=args#>, Func<<#=genArgs#>, TResult> selector)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |  <#
 | 
	
	
		
			
				|  | @@ -108,7 +144,105 @@ for (var i = 2; i <= 15; i++)
 | 
	
		
			
				|  |  |      var res = "(" + string.Join(", ", Enumerable.Range(1, i).Select(j => "IAsyncObserver<T" + j + ">")) + ")";
 | 
	
		
			
				|  |  |      var genPars = string.Join(", ", Enumerable.Range(1, i).Select(j => "T" + j).Concat(new[] { "TResult" }));
 | 
	
		
			
				|  |  |      var genArgs = string.Join(", ", Enumerable.Range(1, i).Select(j => "T" + j));
 | 
	
		
			
				|  |  | +    var tuple = "(" + genArgs + ")";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var all = string.Join(" && ", Enumerable.Range(1, i).Select(j => "values" + j + ".Count > 0"));
 | 
	
		
			
				|  |  | +    var vals = string.Join(", ", Enumerable.Range(1, i).Select(j => "values" + j + ".Dequeue()"));
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +        public static <#=res#> Zip<<#=genArgs#>>(IAsyncObserver<<#=tuple#>> observer)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (observer == null)
 | 
	
		
			
				|  |  | +                throw new ArgumentNullException(nameof(observer));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var gate = new AsyncLock();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +for (var j = 1; j <= i; j++)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +            var values<#=j#> = new Queue<T<#=j#>>();
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +            var isDone = new bool[<#=i#>];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            IAsyncObserver<T> CreateObserver<T>(int index, Queue<T> queue) =>
 | 
	
		
			
				|  |  | +                Create<T>(
 | 
	
		
			
				|  |  | +                    async x =>
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        using (await gate.LockAsync().ConfigureAwait(false))
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            queue.Enqueue(x);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            if (<#=all#>)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                await observer.OnNextAsync((<#=vals#>)).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                            else
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                var allDone = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                for (var i = 0; i < <#=i#>; i++)
 | 
	
		
			
				|  |  | +                                {
 | 
	
		
			
				|  |  | +                                    if (i != index && !isDone[i])
 | 
	
		
			
				|  |  | +                                    {
 | 
	
		
			
				|  |  | +                                        allDone = false;
 | 
	
		
			
				|  |  | +                                        break;
 | 
	
		
			
				|  |  | +                                    }
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                if (allDone)
 | 
	
		
			
				|  |  | +                                {
 | 
	
		
			
				|  |  | +                                    await observer.OnCompletedAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    },
 | 
	
		
			
				|  |  | +                    async ex =>
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        using (await gate.LockAsync().ConfigureAwait(false))
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            await observer.OnErrorAsync(ex).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    },
 | 
	
		
			
				|  |  | +                    async () =>
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        using (await gate.LockAsync().ConfigureAwait(false))
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            isDone[index] = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            var allDone = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            for (var i = 0; i < <#=i#>; i++)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                if (!isDone[i])
 | 
	
		
			
				|  |  | +                                {
 | 
	
		
			
				|  |  | +                                    allDone = false;
 | 
	
		
			
				|  |  | +                                    break;
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            if (allDone)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                await observer.OnCompletedAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +            (
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +for (var j = 1; j <= i; j++)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  |  #>
 | 
	
		
			
				|  |  | +                CreateObserver<T<#=j#>>(<#=j#>, values<#=j#>)<#=(j < i ? "," : "")#>
 | 
	
		
			
				|  |  | +<#
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#>
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          public static <#=res#> Zip<<#=genPars#>>(IAsyncObserver<TResult> observer, Func<<#=genArgs#>, TResult> selector)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (observer == null)
 | 
	
	
		
			
				|  | @@ -137,11 +271,6 @@ for (var j = 1; j <= i; j++)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  #>
 | 
	
		
			
				|  |  |              var isDone = new bool[<#=i#>];
 | 
	
		
			
				|  |  | -<#
 | 
	
		
			
				|  |  | -var all = string.Join(" && ", Enumerable.Range(1, i).Select(j => "values" + j + ".Count > 0"));
 | 
	
		
			
				|  |  | -var vals = string.Join(", ", Enumerable.Range(1, i).Select(j => "values" + j + ".Dequeue()"));
 | 
	
		
			
				|  |  | -var done = "";
 | 
	
		
			
				|  |  | -#>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              IAsyncObserver<T> CreateObserver<T>(int index, Queue<T> queue) =>
 | 
	
		
			
				|  |  |                  Create<T>(
 |