The API is limited to 10 requests per second.
Then just ask the code to make a package of 10 requests, ensuring that they take at least one second:
Items[] items = ...; int index = 0; while (index < items.Length) { var timer = Task.Delay(TimeSpan.FromSeconds(1.2)); // ".2" to make sure var tasks = items.Skip(index).Take(10).Select(i => ProcessItemsAsync(i)); var tasksAndTimer = tasks.Concat(new[] { timer }); await Task.WhenAll(tasksAndTimer); index += 10; }
Update
My ProcessItems method makes 1-4 API calls depending on the element.
In this case, batch processing is not a suitable solution. You need to limit the asynchronous method to a specific number, which implies SemaphoreSlim . The tricky part is that you want to resolve more calls over time.
I have not tried this code, but the general idea I came up with is to have a periodic function that frees up a semaphore up to 10 times. So something like this:
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(10); private async Task<Response> ThrottledSendRequestAsync(HttpRequestMessage request, CancellationToken token) { await _semaphore.WaitAsync(token); return await SendRequestAsync(request, token); } private async Task PeriodicallyReleaseAsync(Task stop) { while (true) { var timer = Task.Delay(TimeSpan.FromSeconds(1.2)); if (await Task.WhenAny(timer, stop) == stop) return;
Using:
// Start the periodic task, with a signal that we can use to stop it. var stop = new TaskCompletionSource<object>(); var periodicTask = PeriodicallyReleaseAsync(stop.Task); // Wait for all item processing. await Task.WhenAll(taskList); // Stop the periodic task. stop.SetResult(null); await periodicTask;
Stephen cleary
source share