不缓存返回值,只防止Promise的重复请求应该怎么做?

893 阅读2分钟

once-init

这篇文章是不用防抖和节流,用更底层的方式解决JS的重复请求的补充。

在这篇文章的评论下面,有人提到应该可以加一个开关, 控制强制跳过缓存。而在once-init中是存在这样的功能的。

下面首先说一下。(看这篇文章之前,你应当先看前一篇文章)

api

refresh

如果你希望用避免重复请求的方式,跳过缓存,这本质上是在刷新缓存,这个行为在once-init中被称作refresh

如下示例,

await oiFoo.init(); // 返回1
// refresh和init在同一时间执行多次,都会阻止重复执行,多余的async function会返回第一次的结果; 
await Promise.all([oiFoo.refresh(), oiFoo.refresh(), oiFoo.refresh()]); // 返回 [2, 2, 2]
// 但refresh如果当前没有其它重复的async function在执行,会刷新结果,并同时刷新缓存(影响到下一次init的返回); 
await Promise.all([oiFoo.refresh(), oiFoo.refresh(), oiFoo.refresh()]); // 返回 [3, 3, 3] 
await oiFoo.init();// 返回 4

refresh 只会阻止 同一时间的重复Promise ,如果同一时间没有重复Promise,则会创建新的Promise达到刷新缓存的能力,重新发出请求的能力;

exceed

如果也不希望避免重复请求,只是想刷新缓存,不在乎是否浪费,则可以使用 exceed

await Promise.all([oiFoo.refresh(), oiFoo.exceed()]); // 50秒后,返回 [1, 2];

await Promise.all([oiFoo.exceed(), oiFoo.refresh()]); // 50秒后,返回 [3, 3];

exceed 不在乎是否有重复 Promise ,它会强制刷新缓存,而refresh如果观察到有重复Promise,则不会执行;

execute

如果你都连缓存也不想刷新,那实际上就是在调用原函数。那你可以调用源函数,也可以用 execute ,它不会更新缓存。

await oiFoo.init(); // 50秒后,返回 1
await oiFoo.execute(); // 50秒后,返回 2
await oiFoo.init(); // 0秒后,返回 1 (缓存未更新)
await oiFoo.refresh(); // 50秒后,返回 3 (缓存更新,重新获取后端值,由于后端在上次execute请求中虽未更新缓存,但更新了后端,所以返回值为3)

原理

下面我说一下refresh的原理,实际上只要不走缓存就可以了。

class OnceInit {
    cache = undefined;
    func;
    promise = undefined;
    constructor(asyncFunc) {
        this.func = asyncFunc;
    }

    async init() {
        if (typeof this.cache !== 'undefined') {
            return this.cache;
        } else {
            if (this.promise) {
                return await this.promise;
            } else {
                const promise = this.func();
                promise.finally(() => {
                    this.promise = undefined;
                })
                this.promise = promise;
                const res = await promise;
                this.cache = res;
                return res;
            }
        }
    }
    
    async refresh() {
        if (typeof this.cache === 'undefined') {
            return await this.init();
        } else {
             /** 下面和 init 是一样的 */
            if (this.promise) {
                return await this.promise;
            } else {
                const promise = this.func();
                promise.finally(() => {
                    this.promise = undefined;
                })
                this.promise = promise;
                const res = await promise;
                this.cache = res;
                return res;
            }
            
        }
    }
}

其它 api 的源码请到 github仓库 中查看。