Rate Limiter Problem
You are given an array of URLs: ['url1', 'url2', ...]
and a limit on the number of simultaneous requests - limit
.
Implement a function that queries the URLs and invokes a callback with an array of responses: ['url1_answer', 'url2_answer', ...]
such that at any given time, no more than limit requests are executed concurrently. As soon as one request completes, the next one should be initiated.
In other words, you need to implement a request queue with
a width equal to limit
.
Requirements:
- The order of the responses in the result array should match the order of the URLs in the input array
- The function should have memoization to avoid querying the same URL more than once
- You can use
fetch
for the requests - There is no need to handle errors
Example
const urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
];
parallelFetch(urls, 2, (responses) => {
console.log(responses);
// ['url1_answer', 'url2_answer', 'url3_answer']
});
Solution
First, let’s understand the problem statement by breaking down it into smaller parts:
- We need to query the URLs and invoke a callback with an array of responses
- At any given time, no more than
limit
requests should be executed concurrently - As soon as one request completes, the next one should be initiated
- The order of the responses in the result array should match the order of the URLs in the input array
- The function should have memoization to avoid querying the same URL more than once
- We can use
fetch
for the requests
Let’s start by creating a function parallelFetch
that takes an array of URLs, a limit, and a callback function as arguments.
function parallelFetch(urls, limit, callback) {
// Your code here
}
Next, we need to implement the logic to handle the requests concurrently. We can use a queue to keep track of the URLs that need to be fetched and a counter to keep track of the number of requests currently in progress.
function parallelFetch(urls, limit, callback) {
const queue = [...urls];
let counter = 0;
}
We can use a recursive function to handle the requests. The function should check if the queue is empty or if the counter has reached the limit. If the queue is empty, we can return early. If the counter has reached the limit, we can wait for the next request to complete before initiating a new one.
function parallelFetch(urls, limit, callback) {
const queue = [...urls];
let counter = 0;
function fetchNext() {
if (queue.length === 0) {
return;
}
if (counter >= limit) {
setTimeout(fetchNext, 100);
return;
}
const url = queue.shift();
counter++;
fetch(url)
.then((response) => response.text())
.then((data) => {
callback(data);
counter--;
fetchNext();
});
}
fetchNext();
}
Finally, we can call the parallelFetch
function with the given URLs, limit, and callback function.
const urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
];
parallelFetch(urls, 2, (responses) => {
console.log(responses);
});
This implementation will query the URLs concurrently with a width equal to the limit and invoke the callback with the responses in the correct order.