如何将利用promise实现一个批量请求,获取资源后逐个分发

在日常的开发中,经常会遇到这样一个场景:

一个页面中有许多的用户头像,为了减少服务端的并发压力,服务端会提供一个批量请求接口。前端批量请求后再通过逻辑做单个用户与头像的关系匹配。

eg:

js 复制代码
getUserAvatar('zhangsan')
getUserAvatar('lisi')

const res = fetchAll(['zhangsan','lisi'])
res = {
    'zhangsan':{
        avatar:'',
        name:'章三'
    }
    // 没有lisi代表lisi不存在
}

// 要能做到通过以上两个接口分最终获取到的是一个promise,
// 但是实际上只请求一次服务端。共同调用fetchAll接口

首先,可以确定的是,要有一个function返回的是一个promise,getUserAvatar就要返回一个promise,且这个promise要在fetchAll结果返回之后被置为resolve或者reject的状态。

那么getUserAvatar是有以下实现:

js 复制代码
function getUserAvatar(id){
    return new Promise((res,rej)=>{
        
    })
}

接下来要考虑的是getUserAvatar被调用后如何触发接口请求?什么时候执行promsieresolve或者reject

首先要满足以下几点:

  1. 100ms内只发一次请求
  2. promise的执行结果要和当前传入的id一一对应。

满足第一点:只发一次请求,是一个类似于节流的实现。那就要用到setTimout实现节流的这一套逻辑。

js 复制代码
getAvatarCloser(deley){
    let timmer;
    return function(id){
        if(!timmer){
            timmer = setTimout(()=>{
                fetchAll()
                clearTimout(timmer);
                timmer = null;
            },[deley])
        }
    }
}

const getUserAvatar= getAvatarCloser(100)

这时候的节流已经处理完毕,但是发现这个时候的getUserAvatar还不能获取到正确的结果,因为fetchAll是一个批量请求接口,这里要怎么把每次调用getUserAvatar传进来的参数组合到一起呢?当然是合理利用节流函数创建的闭包了!利用闭包保存每次调用的参数,在setTimout执行的时候全部取出。

js 复制代码
getAvatarCloser(deley){
    let timmer;
    let requestSet = new Set() // 合理利用set去重
    return function(id){
        if(!requestSet.has(id)){ // 如果还没有请求过,就加入requestSet.等待请求
            requestSet.add(id)
        }
        if(!timmer){
            timmer = setTimout(async ()=>{
                const userInfos = await fetchAll(Array.from(requestSet.values()));
                clearTimout(timmer);
                timmer = null;
            },[deley])
        }
    }
}

const getUserAvatar= getAvatarCloser(100)

这时候,获取到了全部的数据,要怎么给对应的调用返回呢??(满足第二点)

分析一下:

每个promise都需要延迟 到一个异步请求结束后在执行。怎么做到延迟执行呢?

即:在promise外部执行promise的resolve方法

那把promiseresolvereject方法的引用保存在promise外面不就可以做到了吗?

js 复制代码
class DeferredPromise{
    resolve; // 调用这个resolve就是调用promise的resolve
    reject;
    promise;
    constructore(){
        this.promise = new Promise((res,rej)=>{
            this.resolve = res;
            this.reject = rej;
        })
    }
}

利用deferred。当deferredresolve被执行的时候,相当于deferred.promise.resolve被执行。

js 复制代码
getAvatarCloser(deley){
    let timmer;
    let requestSet = new Set() // 合理利用set去重
    let resultMap = new Map();
    return function(id){
        // 保证每次调用getUserAvatar返回的都是一个新的promise
        return new Promise((res,rej)=>{
            if(!requestSet.has(id)){ // 如果还没有请求过,就加入requestSet.等待请求
                requestSet.add(id)
                resultMap.add(id,new DeferredPromise()) 
            }
            if(!timmer){
                timmer = setTimout(async ()=>{
                    const keys = Array.from(requestSet.values())
                    const userInfoRes = await fetchAll(keys);
                    keys.forEach((id) => { 
                        const deferred = deferredMap.get(id); 
                        if (userInfoRes[id]) { 
                            requestSet.delete(+id);
                            // 相当于执行deferred.promise.resolve.
                            deferred.resolve(userInfoRes[id]); 
                        } else { 
                            deferred.reject(new Error(`没找到${id}`)); 
                        }
                    });
                    clearTimout(timmer);
                    timmer = null;
                },[deley])
            }else{
                const currentDef = deferredMap.get(id); 
                // 获取currentDef.promise的结果。并执行当前的promise.resolve
                currentDef.promise.then((data)=>{ 
                    res(rs) 
                  })
                  .catch((e)=>{ rej(e) })
            }
        })
    }
}

const getUserAvatar= getAvatarCloser(100)
相关推荐
诸葛韩信28 分钟前
前端工程化1——npm insatall背后的工作原理
前端·npm·node.js
k***121739 分钟前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
专注前端30年1 小时前
如何使用 HTML5 的 Canvas + JavaScript 实现炫酷的游戏得分特效?
前端·javascript·游戏·html5·canvas·canva可画
q***06291 小时前
解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域
java·前端·spring
木易 士心1 小时前
深入理解 CSS 中的 !important
前端·css
行走的陀螺仪2 小时前
GitLab CI/CD 完整教学指南
前端·ci/cd·gitlab·团队开发·自动化测试部署
谢尔登2 小时前
Webpack高级之常用配置项
前端·webpack·node.js
helloyangkl2 小时前
前端——不同环境下配置env
前端·javascript·react.js
竹秋…2 小时前
webpack搭建react开发环境
前端·react.js·webpack
以明志、2 小时前
并行与并发
前端·数据库·c#