如何将利用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)
相关推荐
啊~哈几秒前
页面弹窗适配问题
前端·javascript·vue.js
工呈士7 分钟前
构建优化策略:Tree Shaking、代码分割与懒加载
前端·面试
骑自行车的码农12 分钟前
React Suspense实现原理深度解析 1
前端·react.js
还是一只小牛15 分钟前
探秘 React Native:线程探索及桥优化
android·前端
Face16 分钟前
Vue源码核心模块解析
前端·vue.js
Canmick16 分钟前
记一次无语的 Vite 构建配置问题排查
前端
FogLetter18 分钟前
从"乱炖"到"法式大餐":Promise如何优雅地管理异步流程
前端·javascript
鹘一18 分钟前
SSE实现deepseek流式输出
前端
xiaok19 分钟前
JavaScript同步与异步执行顺序解析
前端
GIS之路19 分钟前
OpenLayers 图层遮罩与裁剪
前端