This is a variant of WhenAllorError that a colleague asked me about. His scenario was that he had a lot of tasks to complete but since they all involved making HTTP requests to other servers he did not want to start them all at once but rather start a few and then as they completed start a few more. That's how I came up with WhenAllOrErrorBatched. The idea is that instead of giving it a list of tasks you provide a batchSize (number of parallel tasks) and a function that returns new tasks that will be called until it returns null.

 1: public async static Task<T[]> WhenAllOrErrorBatched<T>(
 2:     int batchSize, Func<Task<T>> nextTask)
 3: {
 4:     var result = new List<T>(batchSize);
 5:     var pending = new List<Task<T>>(batchSize);
 6:     bool pendingTasks = true;
 7:     while (true)
 8:     {
 9:         while (pendingTasks && pending.Count < batchSize)
 10:         {
 11:             var task = nextTask();
 12:             if (task == null)
 13:             {
 14:                 pendingTasks = false;
 15:                 break;
 16:             }
 17:  
 18:             pending.Add(task);
 19:         }
 20:  
 21:         if (pending.Count == 0)
 22:         {
 23:             break;
 24:         }
 25:  
 26:         await Task.WhenAny(pending);
 27:  
 28:         for (int i = 0; i < pending.Count; i++)
 29:         {
 30:             if (pending[i].IsCompleted)
 31:             {
 32:                 result.Add(pending[i].Result);
 33:                 pending.RemoveAt(i);
 34:                 i--;
 35:             }
 36:         }
 37:     }
 38:  
 39:     return result.ToArray();
 40: }