减少重复的请求,也许这个库能帮到你
共 6009字,需浏览 13分钟
·
2022-01-08 17:52
点击上方 前端瓶子君,关注公众号
回复算法,加入前端编程面试算法每日一题群
如果你对这个库感兴趣,欢迎
Star
或提请Issue
,也可以成为这个库的贡献者。(如果有人能帮我翻译中转英文档或写测试用例,我将感激不尽)
并不是取消Promise
,而是把多个相同的Promise变成一个! 该执行的函数还是会执行,还是会有返回值,但是不会再生成多余的Promise
了。
请想象下面两个场景
1
有时候,在同一个页面上,存在多个部分,如下图所示。
而在这各个部分中又存在一些重复的数据,例如绿色部分,他们实际上对应的请求的是后端同一个内容。
这个时候,假如我们采取的是对模块的每个组件块都发起一次请求,很明显,这三个可以共享返回值的请求重复了。
对于这三个重复的请求,是否有办法只发起一次请求,然后三个请求共享同一个返回值呢?
2
假设有一个组件提供局部刷新或者加载的功能。
有时候用户点击刷新的时候,因为手抖或者其它未知的原因,总之就是点了不止一下,可能点了好几下,导致直接发送了好几次请求。
对于这好几次请求,是否能够进行整合,只发送了一次请求呢?
once-init
你当然可以自己设计一个复杂的逻辑,封装一个相对安全的组件。又或者,你可以试试 once-init
?
github[1] npm[2]
![npm license](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/743b06903202404f850ce62f6c86ac6e~tplv-k3u1fbpfcp-watermark.awebp)[3] ![npm version](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/962d8f7868cb41ee81d1272600fe4a28~tplv-k3u1fbpfcp-watermark.awebp)[4]
Promise Function Init Once, Use Everywhere.
只会被初始化一次的 Promise
函数。
第一次调用对象的时候会执行
Promise
函数初始化,重复调用,初始化将不会再次执行。
同一个
Promise
不会在同一时间内被执行两次。
压缩后大小
承诺
OnceInit
封装的Promise Function
,永远不会在同一时间被执行两次。如果上一个 Promise Function
没有执行完成就调用了下一个Promise Function
,那么下一个Promise Function
将共享上一个Promise Function
的Promise
。
示例
假设存在一个 axios
Promise
请求,返回值类型为 number
,值为 777
。
const requestNumber = async () => {
const res: AxiosResponse<number> = await axiosInstance.get("/api/number");
return res.data;
};
复制代码
你可以使用 oi
来封装这个 Promise
函数
const oiInstance = oi(requestNumber);
复制代码
现在,你可以在任何地方调用这个实例。
init
假设有两个方法 functionA
和 functionA
,都需要发送这个请求。
async function functionA() {
...
const res = await oiInstance.init();
...
}
async function functionB() {
...
const res = await oiInstance.init();
...
}
复制代码
而你需要在某个文件中,需要同时使用这两个方法。
async function functionC() {
await functionA();
await functionB();
}
function functionD() {
functionA();
functionB();
}
复制代码
对于 functionC
, 在第一次执行 init
之后,oiInstance
将会保存 Promise
的执行结果,此后再执行 init
,将不会再发出 Promise
请求。
对于 functionD
, api
请求只会发送一次,functionA
和 functionB
中的 res
都将等待同一个请求的返回值,不会发送重复的请求。
target
target
能同步获取返回值。
function functionE() {
...
const res = oiInstance.target;
...
}
复制代码
如果在获取 target
之前已经完成初始化,target
的值为 Promise
的返回值,否则,target
的值为 undefined
。例如,
const res = oiInstance.target; // undefined
复制代码
await oiInstance.init();
const res = oiInstance.target; // [Return Value] 777
复制代码
请注意,虽然是同步获取,但 once-init
仍然会认为你此时需要发出请求,因此调用 target
属性也会开始初始化。
在下面这个例子中,我们假设 api
的请求时长是 10s
。在下面这个例子里,请求在第一行的时候就已经发出。
const res = oiInstance.target; // undefined
/** Promise has been executed. */
setTimeout(async () => {
const resAfter = oiInstance.target; // [Return Value] 777
const intAffter = await oiInstance.init(); // [Return Value] 777 , Promise will not be executed again.
/** Since The Promise has been executed before, it will not be executed again. */
}, 10001);
复制代码
和同时先后同步执行两次 init
一样,假如在获取 init
之前访问了 target
属性,而 访问 target
导致的 Promise
请求没有结束的话,init
将直接等待上一个 Promise
结束并返回上一个 Promise
的返回值 。
下面这个例子将会帮助你理解。
const res = oiInstance.target; // undefined
setTimeout(async () => {
const resAfter = oiInstance.target; // undefined
const intAffter = await oiInstance.init(); // [Return Value] 777
/** Since The Promise has been executing it will not be executing again. */
/** After About 8000ms, The Value will be return by the first promise done */
}, 2000);
复制代码
这里的 init
将会等待上一个 Promise
函数执行的返回值,由于 init
是在 200ms
之后才执行的,所以它只需要再等待大约 800ms
就能获得这个返回值了。
defaultValue
使用 target
属性通常需要搭配默认值,而 oi
的第二个参数可以为你的 Promise
定义默认值。
const defaultValue = -1;
const oiInstance = oi(requestNumber, defaultValue);
const ans = oiInstance.target; // -1
复制代码
refresh
你如果想要更新实例的值,则需要调用 refresh
。
假设第一次加载的值是 777
,而刷新之后的值是 888
。
const ans = await oiInstance.init(); // [Retrun Value] 777
const ansAfterRefresh = await oiInstance.refresh(); // [Retrun Value] 888
复制代码
刷新之后,调用 init
和 target
获取的值会变成新的值。
oiInstance.target; // undefined
await oiInstance.init(); // [Promise Retrun Value] 777
oiInstance.target; // 777
await oiInstance.refresh(); // [Promise Retrun Value] 888
/** Promise will not be exectued */
oiInstance.target; // 888
await oiInstance.init(); // 888
复制代码
你可以直接使用 refresh
来执行初始化,在初始化上,它和 init
的效果一致。
oiInstance.target; // undefined
await oiInstance.refresh(); // [Promise Retrun Value] 777
oiInstance.target; // 777
await oiInstance.refresh(); // [Promise Retrun Value] 888
oiInstance.target; // 888
复制代码
如果同步先后调用了两次 refresh
,两次 refresh
将等待同一个请求的返回值,不会发送重复的请求。
async function functionA() {
console.log("A", await oiInstance.refresh());
}
async function functionB() {
console.log("B", await oiInstance.refresh());
}
functionA(); // 'A', [Promise Retrun Value] 777
functionB(); // 'B', [Promise Retrun Value] 777
/** only one promise is executed */
/** functionA and functionB share A same promise and promise return value */
复制代码
我们仍然假设 api
请求的时长为 10s === 10000ms
。
oiInstance.refresh();
setTimeout(async () => {
await oiInstance.refresh();
}, 2000);
/** After 10000ms, two refresh will be exected at the same time */
复制代码
如果异步先后调用了两次 refresh
,那么发送两次请求。
async function functionA() {
console.log("A", await oiInstance.refresh());
}
async function functionB() {
console.log("B", await oiInstance.refresh());
}
await functionA(); // 'A', [Promise Retrun Value] 777
await functionB(); // 'B', [Promise Retrun Value] 888
/** Two different promises were executed */
复制代码
如果你觉得逻辑太过复杂,那请至少要记住一点,OnceInit
封装的 Promise Function
,永远不会在同一时间被执行两次。
除此之外,once-init
还提供了其它的 api
,以满足更多的需求,但以上是它的主要功能。更多信息请查看 once-init 的 github[5] 。
HELP
我还将把发布一个 Vue3-Composition Api
的版本。(预定中)
export abstract class RefOnceInit extends OnceInit<
Ref,
G
> {
loading = ref(false);
protected abstract factory(raw: G, observe: Ref): void;
constructor(defaultValue?: T) {
const refed = ref();
refed.value = defaultValue;
super(refed);
this.onLoading((event) => {
this.loading.value = event;
});
}
}
复制代码
这样 RefOnceInit
就是一个响应式的对象。关于 OnceInit
类请查看源码。
当请求完成的时候,就能引发页面 UI
的变化。
关于本文
来源:Xmo
https://juejin.cn/post/7046667393405304868