这篇文章是不用防抖和节流,用更底层的方式解决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仓库 中查看。