skip to Main Content

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.

2

Answers


  1. Use Promise.all to run a slice items simultaneously in a batch:

    const requestQueue = async (endpoints, callback, limit = 3, offset = 0) => {
    
        const slice = endpoints.slice(offset, offset + limit);
    
        if(!slice.length){
            return;        
        }
    
        // map slice into fetchMock calls and await the batch
        await Promise.all(slice.map(endpoint => fetchMock(endpoint).then(callback).catch(callback)));
    
        // delay between slices, remove if not needed
        await new Promise(resolve => setTimeout(resolve, 1000));
        
        // run the next slice
        return requestQueue(endpoints, callback, limit, offset += limit);
    }
    
    function fetchMock(endpoint) {
        return new Promise(resolve => setTimeout(() => resolve(endpoint), Math.random()*1000));
    }
    
    requestQueue([1, 2, 3, 4, 5], data => console.log(data))
    Login or Signup to reply.
  2. 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.

    Your code can be simplified as below:

    const requestQueue = (endpoints, callback, limit = 3) => {
      while (endpoints.length > 0) {
        const slice = endpoints.splice(
          0,
          limit < endpoints.length ? limit : endpoints.length
        );
    
        return Promise.all(slice)
          .then(callback)
          .then(() => requestQueue(endpoints, callback));
      }
    };
    
    requestQueue([1, 2, 3, 4, 5], data => console.log(data));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search