I tried to write a function that requests only limited amount of mock endpoints at one time. As soon as one request is resolved, the next one is fetching. Here is my code:
const requestQueue = (endpoints, callback, limit = 3) => {
while (endpoints.length > 0) {
if (limit > 0) {
const slice = endpoints.splice(0, limit)
for (const endpoint of slice) {
limit--
fetchMock(endpoint)
.then(data => callback(data))
.catch(err => callback(err))
.finally(() => limit++)
}
}
}
}
function fetchMock(endpoint) {
return Promise.resolve(endpoint)
}
requestQueue([1, 2, 3, 4, 5], data => console.log(data))
It goes in infinite loop and crashes. What am I missing or misunderstaning? Help please.
Reason for infinite loop is limit
variable getting decremented to zero inside the for of
loop; as a result, the while
loop's condition endpoints.length > 0
is never evaluated as false
.
The endpoints.length
is always greater than zero because you don't let the loop enter the if
block after the first iteration. After the first iteration, limit
is zero, so limit > 0
is not true
after the first iteration. As a result, the loop is stuck in an infinite cycle.
Now you might say: "But I have increment the limit
inside the finally
callback, so limit
is not always zero".
For limit
to get incremented, the callback function of finally
has to be executed BUT that never happens because the asynchronous code (callbacks of then
, catch
, and finally
are invoked asynchronously) is executed after synchronous code execution ends BUT in your case, synchronous code execution never ends because it is stuck in the while
loop. You didn't give finally
callback a chance to get executed.
As a matter of fact, none of the promise callbacks are invoked in your code because synchronous code execution has to end for them to be invoked and you are hoping to end the synchronous code execution after asynchronous callbacks are invoked. Your code is stuck in a deadlock.
As you want to initiate the request to another endpoint as soon as any of the initially started requests are completed, following code shows this in action:
let offset = 0;
function makeRequest(endpoints, callback) {
console.log('fetching endpoint ' + endpoints[offset]);
fetchMock(endpoints[offset++])
.then(callback)
.catch(callback)
.finally(() => {
if (offset < endpoints.length) {
requestQueue(endpoints, callback);
}
});
}
const requestQueue = (endpoints, callback, limit = 3) => {
if (offset >= endpoints.length) {
return;
}
if (offset === 0) {
for (let i = 0; i < limit; i++) {
makeRequest(endpoints, callback);
}
} else {
makeRequest(endpoints, callback);
}
};
function fetchMock(endpoint) {
// random delay between 1 to 4 seconds
const randomDelay = (Math.random() * (5 - 1) + 1) * 1000;
return new Promise(res => setTimeout(res, randomDelay, endpoint));
}
requestQueue([1, 2, 3, 4, 5], data => console.log(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }