FE-Interview icon indicating copy to clipboard operation
FE-Interview copied to clipboard

实现一个多并发的请求

Open lgwebdream opened this issue 4 years ago • 3 comments

let urls = ['http://dcdapp.com', …];
/*
	*实现一个方法,比如每次并发的执行三个请求,如果超时(timeout)就输入null,直到全部请求完
	*batchGet(urls, batchnum=3, timeout=3000);
	*urls是一个请求的数组,每一项是一个url
	*最后按照输入的顺序返回结果数组[]
*/

lgwebdream avatar Jul 06 '20 15:07 lgwebdream

扫描下方二维码,获取答案以及详细解析,同时可解锁800+道前端面试题。

lgwebdream avatar Jul 06 '20 15:07 lgwebdream

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    // let urls = ['http://dcdapp.com',];
    /*
      *实现一个方法,比如每次并发的执行三个请求,如果超时(timeout)就输入null,直到全部请求完
      *batchGet(urls, batchnum=3, timeout=3000);
      *urls是一个请求的数组,每一项是一个url
      *最后按照输入的顺序返回结果数组[]
    */
    function batchGet(urls, batchnum = 3, timeout = 3000) {
      return new Promise((resolve) => {
        let results = Array(urls.length).fill()
        let fetchingCount = 0
        let restCount = urls.length
        let finishCount = 0
        let i = 0

        const request = () => {
          while (fetchingCount < batchnum && restCount) {
            fetchingCount++
            restCount--
            let innerIndex = i
            let promises = [
              fetch(urls[innerIndex]).then((res) => {
                return res
              }),
              new Promise((resolve, reject) => {
                let timer = setTimeout(() => {
                  reject(null)
                  clearTimeout(timer)
                  timer = null

                }, timeout)
              })
            ]
            console.log(`🌐 开始请求: ${urls[innerIndex]}`);
            Promise.race(promises).then((res) => {
              console.log(`✔ url-${urls[innerIndex]} %c 成功获取到数据:${res}`, 'background:green;color:#fff;');
              results[innerIndex] = res
            }).catch(errData => {
              console.log(`❌ url-${urls[innerIndex]} %c 超时`, 'background:red;color:#fff;');
              results[innerIndex] = errData
            }).finally(() => {
              fetchingCount--
              finishCount++
              if (finishCount === urls.length) {
                resolve(results)
              }
              request()
            })
            i++
          }
        }

        request()
      })
    }

    // 重新定义 fetch 方便测试
    function fetch(url) {
      const timeout = Math.floor(Math.random() * 1000 * 5)
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(timeout)
        }, timeout)
      })
    }

    const urls=Array(20).fill().map((_,index)=>`https://baidu.com--${index}`)
    batchGet(urls, 5).then((results) => console.log('👉 请求结果:', results));
  </script>
</body>

</html>

AAA611 avatar Sep 05 '22 10:09 AAA611

function delayPromise(timeout) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, timeout);
    });
}

function timeoutPromise(promise, ms) {
    const timeout = delayPromise(ms).then(() => {
        return null;
    });
    return Promise.race[timeout, promise];
}

function fetch(url) {
    return new Promise(async (resolve, reject) => {
        try {
            const res = await fetch(url);
            resolve(res);
        } catch (err) {
            reject(err);
        }
    });
}

function batchGet(urls,batchnum=3, timeout=3000 ) {
    const result = [];
    const chunkLen = Math.ceil(urls.length / batchnum);
    for (let i = 0; i < chunkLen; i++) {
        const curUrls = urls.slice(batchnum * i, batchnum * (i + 1));
        const fetchs = curUrls.map((url) => fetch(url));
        const pAll = Promise.all(fetchs);
        const curRes = timeoutPromise(pAll, timeout);
        if (!curRes) {
            return null;
        }
        result.push(...curRes);
    }
    return result;
}

DENNIE-HONG avatar Dec 23 '23 12:12 DENNIE-HONG