简体   繁体   English

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

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

In my React app I have a component that send request to an online service that is capable to handle 50 requests max.在我的 React 应用程序中,我有一个组件可以将请求发送到最多可以处理 50 个请求的在线服务。 I got a new request now to execute 7000 MAC's.我现在收到一个执行 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();
}, []);

I would like to extend the fetchData function so basically it will send only 50 IP's and will wait till iteration is complete.我想扩展 fetchData 函数,所以基本上它只会发送 50 个 IP 并等待迭代完成。

When the iteration is complete then the next 50 will be executed.当迭代完成后,接下来的 50 次将被执行。

Thank you谢谢

Here is how you can do it without any external libraries:这是没有任何外部库的情况下如何做到这一点:

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;
  }
}

I'm using a generator function* sliceGenerator here to generate the slices of ips array.我在这里使用生成器function* sliceGenerator来生成 ips 数组的切片。 This way you batch process them 50 by 50.通过这种方式,您可以 50 x 50 批量处理它们。

I'm also using a for (... of ...) loop.我也在使用for (... of ...)循环。 It's very convenient because you can use the await keyword inside.这非常方便,因为您可以在里面使用await关键字。

Without library, you could use this function:没有库,您可以使用此功能:

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()) { }
    });
}

This function will take promises from an iterator up to the pool size.此函数将从迭代器中获取承诺直至池大小。 Whenever a promise resolves, it will get the next promise from the iterator so the pool is complete again.每当承诺解决时,它将从迭代器中获取下一个承诺,以便池再次完成。 So the pool does not have to be emptied completely before the next chunk of promises is generated.因此在生成下一个承诺块之前不必完全清空池。 As soon as a spot is free it will be used again.一旦有空位,它就会被再次使用。

It is important that the iterator only creates a next promise when one is pulled from it via the next() method.重要的是,迭代器仅在通过next()方法从中提取时才创建下一个承诺。

In your use case, you can call it as follows:在您的用例中,您可以按如下方式调用它:

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

Note: fetchData does not have to be declared async , since there is no await in there.注意: fetchData不必声明为async ,因为那里没有await

If you don't have any issues in using external library, you can make use of es6-promise-pool to manage concurrent requests as below如果您在使用外部库时没有任何问题,您可以使用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();
}, []);

I am not familiar with axios.all , then I provide a way just use Promise.all .我对axios.all不熟悉,那么我提供了一种使用Promise.all My idea is split the input array to each block 50 addresses, then solve it one by one我的想法是将输入数组拆分为每个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