Breakfast in the morning while walking the nuggets saw an interview question, look at the following big guys each show off their skills, I also itching, picked up my mechanical keyboard a disorderly knocking, found that only need 30 lines of code can be realized, like it is very tired of 👻👻, come with me to turn down 😛

After dinner, I saw @serialcoder’s reply, petrified directly, and carefully read the topic again, and found that it was really wrong topic, too careless 😢 my previous implementation is multiple requests at the same time, but not concurrent, but multiple simultaneous serial. So I started banging again, and it took about half an hour. This should be right, and only used 23 lines, hurriedly also to change the title 😝

The original problem

HTTP requests are queued manually. HTTP requests are queued manually.

Train of thought

Read the topic

Quickly through the topic, you can get the main information :(culprits, read the topic to read carefully, not greedy 😢)

  1. The batch request
  2. Control concurrency
  3. All requests complete. Executecallback

The problem solving

  1. The batch request

In order to realize batch requests, requests do not need to be initiated in order (if necessary, they can be put into the queue in order, or they can be put into the priority queue by priority), so here we can just store in the array, and then iterate, fetching each item in the number and throwing it into the fetch for calling.

  1. Control concurrency

A simple way to control the number of concurrent requests is to slice an array, divide it into segments, and then call another segment after completing one segment. We can do this either recursively or looping, but I think recursion is a little bit more intuitive, so I’m going to do it recursively.

That’s the hard part of this problem, because that’s where I got it wrong. End each request and initiate a new one while controlling the number of concurrent requests. We still use the recursive approach, but this time we add a request queue, and then we just maintain the queue, adding each request we make and throwing each request out, thus controlling the concurrency.

  1. All requests complete. Executecallback

Since it is an asynchronous request, we cannot expect the normal order of installation to be executed after the function call. However, every time the fetch returns a result, then or catch will be called. At this time, we can judge whether the request array is empty to know whether all the functions have been called

Write the topic

Previous ❌ code

(The following can not be ignored, is the previous written incorrect solution)

This step is nothing to say, roll up your sleeves is to knock ~

function handleFetchQueue(urls, max, callback) {
  const requestArr = [];
  urls.forEach((item, idx) = > {
    const i = Math.floor(idx / max);
    if (requestArr[i]) {
      requestArr[i].push(item)
    } else {
      requestArr[i] = [item]
    }
  });

  const handleSubRequests = (subReqs) = > {
    const results = [];
    subReqs.forEach(req= > {
      fetch(req).then(res= > {
        if (results.push(res) === max) {
          if (requestArr.length < 1) {
            'function'= = =typeof callback && callback(results)
          } else {
            handleSubRequests(requestArr.shift(), requestArr, max)
          }
        }
      }).catch(e= > {
        results.push(e)
      })
    })
  };
  handleSubRequests(requestArr.shift())
}
Copy the code

Here are two tips to mention briefly:

  • throughMath.floor(idx / max)We can easily push each item into the correct subarray
  • Make use of array return values:results.push(res)Return the length of the array

Attached is the complete test code:

function handleFetchQueue(urls, max, callback) {
  const requestArr = [];
  urls.forEach((item, idx) = > {
    const i = Math.floor(idx / max);
    if (requestArr[i]) {
      requestArr[i].push(item)
    } else {
      requestArr[i] = [item]
    }
  });

  const handleSubRequests = (subReqs) = > {
    const results = [];
    subReqs.forEach(req= > {
      fetch(req).then(res= > {
        if (results.push(res) === max) {
          if (requestArr.length < 1) {
            'function'= = =typeof callback && callback(results)
          } else {
            handleSubRequests(requestArr.shift(), requestArr, max)
          }
        }
      }).catch(e= > {
        results.push(e)
      })
    })
  };
  handleSubRequests(requestArr.shift())
}


const urls = Array.from({length: 10}, (v, k) => k);

const fetch = function (idx) {
  return new Promise(resolve= > {
    console.log(`start request ${idx}`);
    // Simulate the request time
    const timeout = parseInt(Math.random() * 1e4);
    setTimeout((a)= > {
      console.log(`end request ${idx}`);
      resolve(idx)
    }, timeout)
  })
};

const max = 4;

const callback = (a)= > {
  console.log('run callback');
};

handleFetchQueue(urls, max, callback);
Copy the code

Since I was running in Node, I threw zuo into the browser, so I randomly simulated a fetch function.

✅ code

function handleFetchQueue(urls, max, callback) {
  const urlCount = urls.length;
  const requestsQueue = [];
  const results = [];
  let i = 0;
  const handleRequest = (url) = > {
    const req = fetch(url).then(res= > {
      const len = results.push(res);
      if (len < urlCount && i + 1 < urlCount) {
        requestsQueue.shift();
        handleRequest(urls[++i])
      } else if (len === urlCount) {
        'function'= = =typeof callback && callback(results)
      }
    }).catch(e= > {
      results.push(e)
    });
    if (requestsQueue.push(req) < max) {
      handleRequest(urls[++i])
    }
  };
  handleRequest(urls[i])
}
Copy the code

❌ code and ✅ code test comparison

Error first, can be found in serial execution:

Looking at the correct code, this is concurrency:

Paste the full code:

function handleFetchQueue(urls, max, callback) {
  const urlCount = urls.length;
  const requestsQueue = [];
  const results = [];
  let i = 0;
  const handleRequest = (url) = > {
    const req = fetch(url).then(res= > {
      console.log('Current concurrency:'+requestsQueue);
      const len = results.push(res);
      if (len < urlCount && i + 1 < urlCount) {
        requestsQueue.shift();
        handleRequest(urls[++i])
      } else if (len === urlCount) {
        'function'= = =typeof callback && callback(results)
      }
    }).catch(e= > {
      results.push(e)
    });
    if (requestsQueue.push(req) < max) {
      handleRequest(urls[++i])
    }
  };
  handleRequest(urls[i])
}


const urls = Array.from({length: 10}, (v, k) => k);

const fetch = function (idx) {
  return new Promise(resolve= > {
    console.log(`start request ${idx}`);
    const timeout = parseInt(Math.random() * 1e4);
    setTimeout((a)= > {
      console.log(`end request ${idx}`);
      resolve(idx)
    }, timeout)
  })
};

const max = 4;

const callback = (a)= > {
  console.log('run callback');
};


handleFetchQueue(urls, max, callback);
Copy the code

conclusion

Through reading, solving and writing the three steps (self-created 😛), we can make our thinking very clear, very easy to solve the interview questions.

It took 20 minutes to solve the problem, but it took an hour to write the article. It’s not easy. Please give me a big hand when you think it’s good