如何将利用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)
相关推荐
夜郎king19 分钟前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
夏幻灵1 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_2 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞052 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、2 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao2 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架