繁体   English   中英

如何使用 Promise.all 处理数千个请求

[英]How to use Promise.all to handle thousands of request

在我的 React 应用程序中,我有一个组件可以将请求发送到最多可以处理 50 个请求的在线服务。 我现在收到一个执行 7000 个 MAC 的新请求。

function App() {
const [data, setData] = useState([]);

useEffect(() => {
     const fetchData = async () => {
        await axios.all([
             axios.get("/ipdn/<MAC ADDRESS>", { timeout: 10000 }),
             axios.get("/ipdn/<MAC ADDRESS>", { timeout: 10000 })
       // Adding all the mac address ....... 
        ]).then((responseArr) => {
            setData(responseArr)
        });
     };
    fetchData();
}, []);

我想扩展 fetchData 函数,所以基本上它只会发送 50 个 IP 并等待迭代完成。

当迭代完成后,接下来的 50 次将被执行。

谢谢

这是没有任何外部库的情况下如何做到这一点:

const ips = [
  /* List of mac address. */
];

useEffect(() => {
  const fetchData = async () => {
    const loadedData = [];
    
    // Iterate over the slices of array until all the ips have been processed.
    for (const sliceIps of sliceGenerator(ips)) {
      const gettingData = sliceIps.map(getDataFromIp);
      const sliceLoadedData = await axios.all(gettingData);
      loadedData = loadedData.concat(sliceLoadedData);
    }
    setData(loadedData);
  };
  fetchData();
}, []);

const getDataFromIp = (ip) =>
  axios.get("/ipdn/<MAC ADDRESS>", { timeout: 10000 });

// Generates a slice of an array, here the slice has a size of 50 max.
function* sliceGenerator(arr) {
  const sliceSize = 50;
  let i = 0;
  while (i < arr.length) {
    yield arr.splice(i, i + sliceSize);
    i += sliceSize;
  }
}

我在这里使用生成器function* sliceGenerator来生成 ips 数组的切片。 通过这种方式,您可以 50 x 50 批量处理它们。

我也在使用for (... of ...)循环。 这非常方便,因为您可以在里面使用await关键字。

没有库,您可以使用此功能:

function poolPromises(iterPromises, poolSize) {
    return new Promise((resolve, reject) => {
        let promises = [];
        function nextPromise() {
            let { value, done } = iterPromises.next();
            if (done) {
                resolve(Promise.all(promises));
            } else {
                promises.push(value); // value is a promise
                value.then(nextPromise, reject);
            }
            return !done;
        }
        
        while (promises.length < poolSize && nextPromise()) { }
    });
}

此函数将从迭代器中获取承诺直至池大小。 每当承诺解决时,它将从迭代器中获取下一个承诺,以便池再次完成。 因此在生成下一个承诺块之前不必完全清空池。 一旦有空位,它就会被再次使用。

重要的是,迭代器仅在通过next()方法从中提取时才创建下一个承诺。

在您的用例中,您可以按如下方式调用它:

const fetchData = async () => {
    function * iterRequests() {
        for (let macAddress of macAddresses) {
            yield axios.get("/ipdn/" + macAddress, { timeout: 10000 });
        }
    }
    return poolPromises(iterRequests(), 50).then(setData);
}    

注意: fetchData不必声明为async ,因为那里没有await

如果您在使用外部库时没有任何问题,您可以使用es6-promise-pool来管理并发请求,如下所示

import PromisePool from 'es6-promise-pool';


// macs - Array of mac addresses
useEffect(() => {
  const fetchData = () => {
    const results = [];
    const generatePromises = function*() {
      for (let count = 0; count < macs.length; count++) {
        yield axios.get(`/ipdn/${macs[count]}`, ...);
      }
    }
    const promiseIterator = generatePromises();
    // Create a pool with 10 concurrent requests max
    const pool = new PromisePool(
      promiseIterator,
      10 // Configurable
    );
    // To listen to result
    pool.addEventListener('fulfilled', function (event) {
      console.log('Fulfilled: ' + event.data.result);
      results.push(event.data.result);
    });
    // Start the pool
    pool.start().then(function () {
      setData(results);
      console.log('Complete');
    });
  };
  fetchData();
}, []);

我对axios.all不熟悉,那么我提供了一种使用Promise.all 我的想法是将输入数组拆分为每个block 50个地址,然后一一求解

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    // helper function, split array to chunked array
    const splitToChunks = (items, chunkSize = 50) => {
      const result = [];
      for (let i = 0; i < items.length; i += chunkSize) {
        result.push(items.slice(i, i + chunkSize));
      }
      return result;
    }
    
    const fetchData = async () => {
      const result = []; // init value
      const macAddresses = []; // array of mac addresses - your mac addresses
    
      const chunkedArray = splitToChunks(macAddresses); // return array of array [[...50 mac adds], [], []]
    
      for (const macs of chunkedArray) { // now macs is [...50 mac adds]
        const promises = macs.map((mac) => {
          return axios.get(`/ipdn/${mac}`, { timeout: 10000 });
        });
        // now promises is array contains 50 Promises
        const response = await Promise.all(promises); // wait until finish 50 requests
        result.push(...response); // copy response to result, and continue for next block
      }
    
      setData(result);
    };
    fetchData();
  }, []);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM