減少重復(fù)的請(qǐng)求,也許這個(gè)庫(kù)能幫到你
點(diǎn)擊上方?前端瓶子君,關(guān)注公眾號(hào)
回復(fù)算法,加入前端編程面試算法每日一題群
如果你對(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ì)開(kāi)始初始化。
在下面這個(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?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;
????});
??}
}
復(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

