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: }