最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 场景题-请求合并 | 刷题打卡

    正文概述 掘金(粥里有勺糖)   2021-03-06   571

    题目描述

    短时间内需要请求多个同类型的资源

    期望多个请求合并成一个请求发送

    例如:

    • 有一个接口其请求路径为 /path
    • query有一个id参数支持传一个或者多个id
      • /path?id=1
      • /path?id=1,2,3
    • 对应的响应格式为
    {
        code:0,
        data:{
            1:{},
        },
        errMsg:'success'
    }
    // or
    {
        code:0,
        data:{
            1:{},
            2:{},
            3:{}
        },
        errMsg:'success'
    }
    

    request 方法示例

    request({
        url:'/path',
        query:{
            id: '0'
        }
    })
    

    要求

    实现一个 getArticle 方法,每个方法回调最终拿到的是自己需要的内容,且短时间内只发出了一次请求

    getArticle(3).then(res=>{})
    getArticle(4).then(res=>{})
    getArticle(5).then(res=>{})
    getArticle(6).then(res=>{})
    
    // request({
    //     url:'/path',
    //     query:{
    //         id:'3,4,5,6'
    //     }
    // })
    

    题目分析

    场景分析

    常见需要短时间请求多个同类型资源的场景就是资源懒加载的时候

    • 如:一个文章列表的中,获取每个展示文章点赞/评论数是单独的一个接口
    • 当一次性需要展示多条新闻,那么就要发起多个查询点赞/评论的请求

    后端为了减少处理请求的压力,通常会让一个接口支持同时查询多条数据的能力

    考点

    1. 怎么确定这个短时间是多久?
      • 其实这里就是考察到了event loop,这个短时间就是指同一个周期内,然后就变成了合并同一个周期内的请求,如何保证在一个周期内,这里就可以用到防抖,让请求在执行宏任务的时候发出
    2. 如何让每个方法拿到自己需要的数据?
      • 这里可以在方法内部用一个map将每个方法的Promise的resolve存起来,每个方法传参的id作为key,在接口响应后,将对应数据通过key取出,然后从map中取出对应Promise的resolve然后执行resolve(data[id])即可

    需要考虑的问题

    1. 如果多个请求参数是一样的那么,最终请求的参数只有一个
      • 如 连续调用两次 getArticle(3)
      • 那么 请求的query依旧是 {id:'3'}而不是 {id:'3,3'}
      • 并且这两个请求的方法都需要得到响应
    2. 如果这个请求没有被按时响应,不能影响下一次发送

    代码实现

    var getArticle = (function () {
        let timer = null;
        let resolveMap = new Map();
        return function (id) {
            return new Promise((resolve) => {
                // 这里用string类型作为key
                const key = `${id}`;
                const resolves = resolveMap.get(key);
    
                // 不存在则创建,因为可能有重复的id,所以这里value为数组
                if (!resolves) {
                    resolveMap.set(key, [resolve]);
                } else {
                    // 存在则加入,因为是对象,map里存的引用,所以这里不需要重新执行set
                    resolves.push(resolve)
                }
    
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    // 这里将把请求发出去,需要重置状态
                    // 所以将现有的保存下来
                    const _resolvesMap = resolveMap
    
                    const keys = [..._resolvesMap.keys()]
                    request({
                        url: '/path',
                        query: keys.join(',')
                    }).then(res => {
                        const { data } = res
                        // 执行resolve
                        for (const key of keys) {
                            const resolves = _resolvesMap.get(key)
                            const v = data[key]
                            resolves.forEach(r => r(v))
                        }
                    })
    
    
                    // 请求发出后就初始化,以便用于下次请求
                    timer = null;
                    resolveMap = new Map();
                })
            });
        };
    })();
    

    测试

    模拟实现一个request

    function request(options = {}) {
        console.log(new Date(), '发起一次请求', '-------参数为:', options.query)
        return new Promise(res => {
            const { query } = options
            if (!query) {
                res({ data: {} })
                return
            }
    
            const ids = query.split(',')
            const testData = ids.reduce((pre, id) => {
                pre[id] = {
                    id,
                    rand: Math.random()
                }
                return pre
            }, {})
    
            // 模拟响应延迟
            setTimeout(() => {
                res({
                    code: 0,
                    data: testData,
                    errMsg: 'ok'
                })
            }, 500)
        })
    }
    

    测试用例

    getArticle(1).then(console.log)
    getArticle(3).then(console.log)
    getArticle(2).then(console.log)
    getArticle(2).then(console.log)
    getArticle(1).then(console.log)
    
    new Promise((res) => {
        getArticle(4).then(console.log)
        res()
    })
    
    setTimeout(() => {
        getArticle(1).then(console.log)
        getArticle(3).then(console.log)
        getArticle(2).then(console.log)
        getArticle(2).then(console.log)
        getArticle(1).then(console.log)
    }, 400)
    

    打印结果

    场景题-请求合并 | 刷题打卡

    总结

    1. 考察知识点 event loop,防抖,Promise
    2. 这是一个很常见的业务问题,考察面试者的动手实践能力

    本文正在参与「掘金 2021 春招闯关活动」, 点击查看


    起源地下载网 » 场景题-请求合并 | 刷题打卡

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元