如何将利用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)
相关推荐
LiaCode19 分钟前
别让 AI 把仓库写成“代码平行宇宙”:从 Deslop 看生成前查重
前端·后端·github
nice先生的狂想曲30 分钟前
javascript高级程序设计(六)——2026.9.5
前端·javascript
光影少年40 分钟前
如何做大型React+RN 项目架构设计、目录规范
前端·react native·react.js
一位正在转型AI全栈的前端工程师1 小时前
AI 全栈学习之旅 -Week 8:从单 Agent 到多 Agent 协作:LangGraph 实战与记忆持久化
前端·python
用户921080262861 小时前
Promise 和 async/await:从用途到执行机制
前端
yyt3630458411 小时前
KLineChartQuant:GLSL 的精度问题及 RTC 解法
前端·vue.js·react.js·交互·图形渲染·webgl·数据可视化
HarmonLTS1 小时前
智盾 WAF v8.2 Ultra|下一代 Web 应用防火墙
前端
এ慕ོ冬℘゜1 小时前
前端实战:基于jQuery递归实现通用树形组织菜单(可直接复用)
前端·javascript·jquery
计算机魔术师1 小时前
英伟达据报洽谈向 Mira Murati 的 Thinking Machines Lab 投资 25 亿美元
前端
变与不变8062 小时前
js函数与封装详细解答
前端·javascript·vue.js