減少重復(fù)的請(qǐng)求,也許這個(gè)庫(kù)能幫到你
點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)
回復(fù)加群,加入前端Q技術(shù)交流群
如果你對(duì)這個(gè)庫(kù)感興趣,歡迎
Star或提請(qǐng)Issue,也可以成為這個(gè)庫(kù)的貢獻(xiàn)者。(如果有人能幫我翻譯中轉(zhuǎn)英文檔或?qū)憸y(cè)試用例,我將感激不盡)
并不是取消Promise,而是把多個(gè)相同的Promise變成一個(gè)! 該執(zhí)行的函數(shù)還是會(huì)執(zhí)行,還是會(huì)有返回值,但是不會(huì)再生成多余的Promise了。
請(qǐng)想象下面兩個(gè)場(chǎng)景
1
有時(shí)候,在同一個(gè)頁(yè)面上,存在多個(gè)部分,如下圖所示。
而在這各個(gè)部分中又存在一些重復(fù)的數(shù)據(jù),例如綠色部分,他們實(shí)際上對(duì)應(yīng)的請(qǐng)求的是后端同一個(gè)內(nèi)容。
這個(gè)時(shí)候,假如我們采取的是對(duì)模塊的每個(gè)組件塊都發(fā)起一次請(qǐng)求,很明顯,這三個(gè)可以共享返回值的請(qǐng)求重復(fù)了。
對(duì)于這三個(gè)重復(fù)的請(qǐng)求,是否有辦法只發(fā)起一次請(qǐng)求,然后三個(gè)請(qǐng)求共享同一個(gè)返回值呢?
2
假設(shè)有一個(gè)組件提供局部刷新或者加載的功能。
有時(shí)候用戶點(diǎn)擊刷新的時(shí)候,因?yàn)槭侄痘蛘咂渌粗脑颍傊褪屈c(diǎn)了不止一下,可能點(diǎn)了好幾下,導(dǎo)致直接發(fā)送了好幾次請(qǐng)求。
對(duì)于這好幾次請(qǐng)求,是否能夠進(jìn)行整合,只發(fā)送了一次請(qǐng)求呢?
once-init
你當(dāng)然可以自己設(shè)計(jì)一個(gè)復(fù)雜的邏輯,封裝一個(gè)相對(duì)安全的組件。又或者,你可以試試 once-init ?
github[1] npm[2]
[3] [4]
Promise Function Init Once, Use Everywhere.
只會(huì)被初始化一次的 Promise 函數(shù)。
第一次調(diào)用對(duì)象的時(shí)候會(huì)執(zhí)行
Promise函數(shù)初始化,重復(fù)調(diào)用,初始化將不會(huì)再次執(zhí)行。
同一個(gè)
Promise不會(huì)在同一時(shí)間內(nèi)被執(zhí)行兩次。
壓縮后大小
承諾
-
OnceInit封裝的Promise Function,永遠(yuǎn)不會(huì)在同一時(shí)間被執(zhí)行兩次。 -
如果上一個(gè) Promise Function沒(méi)有執(zhí)行完成就調(diào)用了下一個(gè)Promise Function,那么下一個(gè)Promise Function將共享上一個(gè)Promise Function的Promise。
示例
假設(shè)存在一個(gè) axios Promise 請(qǐng)求,返回值類型為 number ,值為 777。
const requestNumber = async () => {
const res: AxiosResponse<number> = await axiosInstance.get("/api/number");
return res.data;
};
復(fù)制代碼
你可以使用 oi 來(lái)封裝這個(gè) Promise 函數(shù)
const oiInstance = oi(requestNumber);
復(fù)制代碼
現(xiàn)在,你可以在任何地方調(diào)用這個(gè)實(shí)例。
init
假設(shè)有兩個(gè)方法 functionA 和 functionA,都需要發(fā)送這個(gè)請(qǐng)求。
async function functionA() {
...
const res = await oiInstance.init();
...
}
async function functionB() {
...
const res = await oiInstance.init();
...
}
復(fù)制代碼
而你需要在某個(gè)文件中,需要同時(shí)使用這兩個(gè)方法。
async function functionC() {
await functionA();
await functionB();
}
function functionD() {
functionA();
functionB();
}
復(fù)制代碼
對(duì)于 functionC, 在第一次執(zhí)行 init 之后,oiInstance 將會(huì)保存 Promise 的執(zhí)行結(jié)果,此后再執(zhí)行 init ,將不會(huì)再發(fā)出 Promise 請(qǐng)求。
對(duì)于 functionD, api 請(qǐng)求只會(huì)發(fā)送一次,functionA 和 functionB 中的 res 都將等待同一個(gè)請(qǐng)求的返回值,不會(huì)發(fā)送重復(fù)的請(qǐng)求。
target
target 能同步獲取返回值。
function functionE() {
...
const res = oiInstance.target;
...
}
復(fù)制代碼
如果在獲取 target 之前已經(jīng)完成初始化,target 的值為 Promise 的返回值,否則,target 的值為 undefined 。例如,
const res = oiInstance.target; // undefined
復(fù)制代碼
await oiInstance.init();
const res = oiInstance.target; // [Return Value] 777
復(fù)制代碼
請(qǐng)注意,雖然是同步獲取,但 once-init 仍然會(huì)認(rèn)為你此時(shí)需要發(fā)出請(qǐng)求,因此調(diào)用 target 屬性也會(huì)開始初始化。
在下面這個(gè)例子中,我們假設(shè) api 的請(qǐng)求時(shí)長(zhǎng)是 10s 。在下面這個(gè)例子里,請(qǐng)求在第一行的時(shí)候就已經(jīng)發(fā)出。
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);
復(fù)制代碼
和同時(shí)先后同步執(zhí)行兩次 init 一樣,假如在獲取 init 之前訪問(wèn)了 target 屬性,而 訪問(wèn) target 導(dǎo)致的 Promise 請(qǐng)求沒(méi)有結(jié)束的話,init 將直接等待上一個(gè) Promise 結(jié)束并返回上一個(gè) Promise 的返回值 。
下面這個(gè)例子將會(huì)幫助你理解。
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);
復(fù)制代碼
這里的 init 將會(huì)等待上一個(gè) Promise 函數(shù)執(zhí)行的返回值,由于 init 是在 200ms 之后才執(zhí)行的,所以它只需要再等待大約 800ms 就能獲得這個(gè)返回值了。
defaultValue
使用 target 屬性通常需要搭配默認(rèn)值,而 oi 的第二個(gè)參數(shù)可以為你的 Promise 定義默認(rèn)值。
const defaultValue = -1;
const oiInstance = oi(requestNumber, defaultValue);
const ans = oiInstance.target; // -1
復(fù)制代碼
refresh
你如果想要更新實(shí)例的值,則需要調(diào)用 refresh 。
假設(shè)第一次加載的值是 777 ,而刷新之后的值是 888 。
const ans = await oiInstance.init(); // [Retrun Value] 777
const ansAfterRefresh = await oiInstance.refresh(); // [Retrun Value] 888
復(fù)制代碼
刷新之后,調(diào)用 init 和 target 獲取的值會(huì)變成新的值。
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
復(fù)制代碼
你可以直接使用 refresh 來(lái)執(zhí)行初始化,在初始化上,它和 init 的效果一致。
oiInstance.target; // undefined
await oiInstance.refresh(); // [Promise Retrun Value] 777
oiInstance.target; // 777
await oiInstance.refresh(); // [Promise Retrun Value] 888
oiInstance.target; // 888
復(fù)制代碼
如果同步先后調(diào)用了兩次 refresh ,兩次 refresh 將等待同一個(gè)請(qǐng)求的返回值,不會(huì)發(fā)送重復(fù)的請(qǐng)求。
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 */
復(fù)制代碼
我們?nèi)匀患僭O(shè) api 請(qǐng)求的時(shí)長(zhǎng)為 10s === 10000ms 。
oiInstance.refresh();
setTimeout(async () => {
await oiInstance.refresh();
}, 2000);
/** After 10000ms, two refresh will be exected at the same time */
復(fù)制代碼
如果異步先后調(diào)用了兩次 refresh ,那么發(fā)送兩次請(qǐng)求。
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 */
復(fù)制代碼
如果你覺(jué)得邏輯太過(guò)復(fù)雜,那請(qǐng)至少要記住一點(diǎn),OnceInit 封裝的 Promise Function ,永遠(yuǎn)不會(huì)在同一時(shí)間被執(zhí)行兩次。
除此之外,once-init 還提供了其它的 api ,以滿足更多的需求,但以上是它的主要功能。更多信息請(qǐng)查看 once-init 的 github[5] 。
HELP
我還將把發(fā)布一個(gè) Vue3-Composition Api 的版本。(預(yù)定中)
export abstract class RefOnceInit<T, G = T> extends OnceInit<
Ref<T | undefined>,
G
> {
loading = ref<boolean>(false);
protected abstract factory(raw: G, observe: Ref<T | undefined>): void;
constructor(defaultValue?: T) {
const refed = ref<T>();
refed.value = defaultValue;
super(refed);
this.onLoading((event) => {
this.loading.value = event;
});
}
}
復(fù)制代碼
這樣 RefOnceInit 就是一個(gè)響應(yīng)式的對(duì)象。關(guān)于 OnceInit 類請(qǐng)查看源碼。
當(dāng)請(qǐng)求完成的時(shí)候,就能引發(fā)頁(yè)面 UI 的變化。
關(guān)于本文
來(lái)源:Xmo
https://juejin.cn/post/7046667393405304868
往期推薦
最后
歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...
歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專業(yè)的技術(shù)人...

