// ApiCalls.cs using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Beam.Api; /// /// Executes a batch of s using either sequential or parallel strategy. /// public sealed class ApiCalls { private readonly IReadOnlyList _calls; private readonly int _maxDegree; internal ApiCalls(IReadOnlyList calls, int? maxDegree) { _calls = calls ?? throw new ArgumentNullException(nameof(calls)); _maxDegree = Math.Max(1, maxDegree ?? 1); } /// /// Runs every call and returns the ordered list of s. /// public async Task> ExecuteAsync( ILogger? logger = null, (int @try, int max)? tries = null, CancellationToken ct = default) { if (_maxDegree == 1) { // sequential var sequential = new List(_calls.Count); foreach (var call in _calls) sequential.Add(await call.GetResponse(logger, tries, ct)); return sequential; } // parallel var bag = new ConcurrentBag<(int idx, ApiResponse res)>(); await Parallel.ForEachAsync( _calls.Select((c, i) => (call: c, idx: i)), new ParallelOptions { MaxDegreeOfParallelism = _maxDegree, CancellationToken = ct }, async (item, token) => { var response = await item.call.GetResponse(logger, tries, token); bag.Add((item.idx, response)); }); // keep original ordering return bag.OrderBy(x => x.idx).Select(x => x.res).ToList(); } }