「面试官问」批量请求数据实现-限制并发量

·  阅读 2802

前言

分享一首bgm进入正题咯:普普通通

并发请求的处理,说起来也算是一道必问题,网上其实已经有很多答案了各种做法,但自己做个记录,来吧我们直接上题。

请实现如下函数,可以批量请求数据,所有 URL 地址在urls参数中,同时可以通过max参数控制请求的并发度,当所有请求执行结束后需要执行callback回调函数,请求函数用 fetch 即可(不做请求失败处理)。

司尘:我们普普通通 脑袋空空 题看不懂 望向天空 skr~~~

理理思路

简化问题

面试官:看你脑袋空空,给你简化一下问题吧,我们不管max值你先实现请求完执行callback

function requestData(urls = [], callback = ()=>{}) {
  let requestArr = []
  for(let i = 0, length = urls.length; i < length; i++) {
    // fetch的用法可以直接MDN看一下吼
    requestArr.push(fetch(urls[i]))
  }
  Promise.all(requestArr)).then(() => {
    callback();
  }).catch(err => {
    console.log(err)
  })
}
复制代码

面试官:额,这应该是最粗暴的办法了,那你把max限制加上,然后转换一下思路再看一下怎么完成题目咧,提示一下可以用递归。

机智的读者:哦哦哦,懂了交给我了

具体流程

  • 根据max值我们建立一个请求的执行数组requestArr(长度上限就为max了)

  • 我们将请求的promise一项一项丢入到其中(递归实现),执行完毕就删除掉

  • requestArr到达上限那么我们等待请求执行,有空位在继续添加

  • urls添加完毕之后我们将requestArr(这里只需要拿到最后的请求数组是因为,之前请求执行完了才会自己删除自己,所以只需要判定最后的是否都执行完就ok了)进行Promise.all判定执行callback。

上代码

戴好眼镜好好看哦

function requestData(urls = [], max = 1, callback = ()=>{}) {
    let requestArr = [],
        i = 0;
    function toFetch({
        if (i === urls.length) {
            // 判定都添加完后返回resolve
            return Promise.resolve();
        }

        // 通过fetch方法创建请求promise
        let fetchItem = fetch(urls[i++]);
        requestArr.push(fetchItem);
        // 给每一项定义一个执行完毕删除自身的微任务
        fetchItem.then( () => {requestArr.splice(requestArr.indexOf(fetchItem), 1)});
        
        let result = Promise.resolve();
        if (requestArr.length === max) {
            // 当执行数组达到上限时我们通过Promise.race方法判定是否有“空位置”
            result = Promise.race(requestArr);
        }
        return result.then(() => toFetch());
    }
    
    toFetch().then(() => Promise.all(requestArr)).then(() => {
        callback();
    })
}
复制代码

升级一下

其实这个题目他主要思想就是当我们任务队列满了的时候就停住嘛,那我们Promise对象使用.then的话那就只能通过递归去实现了,我们这不是还有await嘛,通过await我们就可以直接在任务队列满掉的时候去判断停住函数。来康代码

async function requestData(urls = [], max = 1, callback = ()=>{}) {
  // 这边直接定义一个数组接受所有fetch request
  const fetchArr = []
  const requestArr = []
  for(const item of urls) {
    const p = fetch(item)
    fetchArr.push(p)
    // 如果最大限制比数组小我们才需要走这一步
    if(max <= urls.length) {
      const e = p.then(() => {
        requestArr.splice(requestArr.indexOf(p), 1)
      })
      requestArr.push(e)
      if (requestArr.length >= max) {
        await Promise.race(requestArr);
      }
    }
  }
  Promise.all(fetchArr)).then(() => {
    callback()
  })

复制代码

这样我们似乎更巧妙的实现了这个题目。

总结

其实我个人感觉实际应用中这种场景还是比较少的(当然可能也是我见得太少了),但是面试中这也是个高频题了吼,感觉主要还是说考察promise的实际用法。冷风萧瑟,堵在公司,加班不痛,打车才痛,干饭人,冲!!!

分类:
前端
标签: