js中通过promise对象实现并发请求合并,同时对数据进行扇出

122 阅读2分钟

问题由来

通常前后端交互时候,会有一个token,这个token是有有效期和刷新机制的。

一般刷新策略是在每次api请求时候检查token是否将要过期,从而决定是否刷新。

这里就有一个问题了,刚加载一个页面时候,会有好几个api并发进行GET请求,如果token时间恰好达到刷新时间,那么会请求好几遍刷新token的接口,虽然最终存储的是一个token,但是请求好几次,很不优雅。

这种情况就可以使用promise对象来构造扇入扇出代码,从而在并发时候,保证只有一次请求token刷新。

代码复现


//定义token刷新接口
async function refrestoken(index){
    //模拟获取token
    console.log("GET token_" + index)
    //模拟存储token
    console.log("save token_" + index)
    //返回token
    return "token_"+index
};

//并发请求刷新
(async function(){
    let token = await refrestoken(1)
    console.log(token)
})();
//并发请求刷新
(async function(){
    let token = await refrestoken(2)
    console.log(token)
})();

//并发请求刷新
(async function(){
    let token = await refrestoken(3)
    console.log(token)
})()

代码解析:

  • promise对象除了用new Promise() 还可以使用async function(){}函数式方式实现
  • 该代码模拟了请求刷新token接口,同时并发请求三个token刷新接口。
  • 改代码打印结果如下
GET token_1 
save token_1
GET token_2
save token_2
GET token_3
save token_3
token_1
token_2
token_3

虽然最后token只存储并使用token_3,系统运行没问题,但是多请求了两次token。

实现请求合并并对token进行扇出

let tokenPromise = null;

async function refrestoken(index){
    //模拟获取token
    console.log("GET token_" + index)
    //模拟存储token
    console.log("save token_" + index)
    //返回token
    return "token_"+index

};

(async function(){

    if(tokenPromise == null){
        //重点 promise对象也可以重复使用
        tokenPromise = refrestoken(1)
        let token = await tokenPromise;
        console.log(token)
        tokenPromise = null;
    }else{
        let token = await tokenPromise;
        console.log(token)
    }
})();


(async function(){
    if(tokenPromise == null){
        tokenPromise = refrestoken(2)
        let token = await tokenPromise;
        console.log(token)
        tokenPromise = null;
    }else{
        let token = await tokenPromise;
        console.log(token)
    }
})();


(async function(){
    if(tokenPromise == null){
        tokenPromise = refrestoken(3)
        let token = await tokenPromise;
        console.log(token)
        tokenPromise = null;
    }else{
        let token = await tokenPromise;
        console.log(token)
    }

})()

代码解析:

  • 返回结果:
 GET token_1
 save token_1 
 token_1 
 token_1 
 token_1 

通过打印结果可以看出,只调用一次token刷新接口,同时并发的三个请求接口都获取到同一个token结果。

  • 重点代码

tokenPromise = refrestoken(1); let token = await tokenPromise;

  • promise是对象,重复进行await调用,并且每次时候,会阻塞,直到promise返回结。