優化 http 併發請求

問題

現有 40 個異步請求須要發送,但因爲某些緣由,咱們必須將同一時刻併發請求數量控制在 6 個之內,同時還要儘量快速的拿到響應結果。應該怎麼作?面試

這個問題與一道經典面試題很相似:數組

實現一個批量請求函數 multiRequest(urls, maxNum),要求以下:
• 要求最大併發數 maxNum
• 每當有一個請求返回,就留下一個空位,能夠增長新的請求
• 全部請求完成後,結果按照 urls 裏面的順序依次打出

實現

Promise 串行與並行

  • 串行:一個異步請求完了以後在進行下一個請求;
  • 並行:多個異步請求同時進行;

串行

串行是一個 http 請求成功後再次發起下一個 http 請求;promise

優勢瀏覽器

  • http 請求是有序的;
  • 後一個 http 請求能夠拿到前一個請求的返回值;

缺點:併發

  • 沒有利用瀏覽器同域名請求的最大併發數,同時只會存在一個 http 請求,很大程度上延長了響應時間。
const p = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("1000");
      resolve();
    }, 1000);
  });
};
const p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("2000");
      resolve();
    }, 2000);
  });
};
const p2 = function () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("3000");
      resolve();
    }, 3000);
  });
};

p().then(() => {
    return p1();
  }).then(() => {
    return p2();
  }).then(() => {
    console.log("end");
  });

並行

參數數組中全部 promise 都達到resolve狀態,才執行then回調。異步

缺點:函數

  • 若是 http 請求達到幾萬條,promise.all 在瞬間發出幾萬條 http 請求,這樣頗有可能致使堆積了無數調用棧致使內存溢出。
  • 任意一個 promise 是 reject,就不會進入 then;
const promises = () => {
  return [1000, 2000, 3000].map((current) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(current);
        resolve();
      }, current);
    });
  });
};

Promise.all(promises()).then(() => {
  console.log("end");
});

Promise.all 併發限制

Promise.all 併發限制指的是:每一個時刻併發執行的promise數量是固定的,最終的執行結果仍是保持與原來的Promise.all一致。fetch

栗子: 請求接口相同,參數不一樣的請求併發限制

添加最大併發數 maxRequestNum,全部請求完成後再返回。url

優勢:code

  • 利用瀏覽器同域名請求的最大併發數,快速拿到全部返回值,減小響應時間,不會形成堆棧溢出。

缺點:

  • 全部請求所有結束纔會返回,白屏時間久,用戶體驗很差。
const multiRequest = (fetch, params = [], maxRequestNum = 6) => {
  const paramsLength = params.length;
  let result = new Array(paramsLength).fill(false);
  let sendCount = 0;
  let finishCount = 0;

  return new Promise((resolve) => {
    while (sendCount < maxRequestNum && sendCount < paramsLength) {
      next();
    }

    function handleResult(current, res) {
      finishCount ++;
      result[current] = res;
      if (sendCount < paramsLength) {
        next();
      }
      if (finishCount >= paramsLength) {
        resolve(result);
      }
    }

    function next() {
      let current = sendCount++;
      const param = params[current];
      fetch(param).then((res) => {
        handleResult(current, res)
      }).catch((err) => {
        handleResult(current, err)
      });
    }
  });
}
相關文章
相關標籤/搜索