這里給大家分享我在網(wǎng)上總結(jié)出來的一些知識(shí),希望對(duì)大家有所幫助
我們經(jīng)常會(huì)遇到一個(gè)場(chǎng)景,比如在一個(gè)列表中批量獲取用戶的信息。
如果我們一次性往后端發(fā)送幾十條請(qǐng)求是非常愚蠢的事情。此時(shí)我們就要學(xué)會(huì)如何使用批量獲取的邏輯。
但是批量獲取有一個(gè)問題就是,我需要在用戶列表項(xiàng)的上層去獲取,然后再把結(jié)果分發(fā)給下層
此時(shí)的結(jié)構(gòu)如下:
const List = () => { return itemInfoList.map((info) => <Item info={info} />) }
這樣我們就可以很方便的解決我們遇到的問題啦,因?yàn)槭且粋€(gè)接口獲取的結(jié)果嘛。
但是!這種寫法不利于維護(hù)。因?yàn)?<Item />
組件的依賴過于龐大,是一個(gè)完整的對(duì)象。對(duì)于其他組件來說很難復(fù)用,大概率這個(gè)組件就只能在一處地方用了。那么我想要復(fù)用怎么辦呢?
那就是寫成如下形式
const List = () => { return ids.map((id) => <Item id={id} />) }
然后 <Item />
組件內(nèi)部就可以根據(jù)傳入的id自行獲取對(duì)應(yīng)的數(shù)據(jù),然后自我處理了。
那么不就遇到開頭這個(gè)問題了嘛?如果一次渲染幾十個(gè)用戶組件,那么不就同時(shí)向后端發(fā)送幾十個(gè)網(wǎng)絡(luò)請(qǐng)求了嘛!
這時(shí)候我們需要實(shí)現(xiàn)一個(gè)邏輯,自動(dòng)收集并合并可以被合并的網(wǎng)絡(luò)請(qǐng)求。
完整代碼如下:
interface QueueItem<T, R> { params: T; resolve: (r: R) => void; reject: (reason: unknown) => void; } /** * 創(chuàng)建一個(gè)自動(dòng)合并請(qǐng)求的函數(shù) * 在一定窗口期內(nèi)的所有請(qǐng)求都會(huì)被合并提交合并發(fā)送 * @param fn 合并后的請(qǐng)求函數(shù) * @param windowMs 窗口期 */ export function createAutoMergedRequest<T, R>( fn: (mergedParams: T[]) => Promise<R[]>, windowMs = 200 ): (params: T) => Promise<R> { let queue: QueueItem<T, R>[] = []; let timer: number | null = null; async function submitQueue() { timer = null; // 清空計(jì)時(shí)器以接受后續(xù)請(qǐng)求 const _queue = [...queue]; queue = []; // 清空隊(duì)列 try { const list = await fn(_queue.map((q) => q.params)); _queue.forEach((q1, i) => { q1.resolve(list[i]); }); } catch (err) { _queue.forEach((q2) => { q2.reject(err); }); } } return (params: T): Promise<R> => { if (!timer) { // 如果沒有開始窗口期,則創(chuàng)建 timer = window.setTimeout(() => { submitQueue(); }, windowMs); } return new Promise<R>((resolve, reject) => { queue.push({ params, resolve, reject, }); }); }; }
用法是:
const fetchUserInfo = createAutoMergedRequest<string, UserBaseInfo>( async (userIds) => { const { data } = await request.post('/api/user/getUserInfoList', { userIds, }); return data; } ); fetchUserInfo(1) fetchUserInfo(2) fetchUserInfo(3)
接下來我們來解讀一下代碼。
先看整體架構(gòu)。 createAutoMergedRequest
函數(shù)返回了一個(gè)匿名函數(shù),來接受參數(shù)并返回結(jié)果請(qǐng)求。但是需要注意的是我們定義了兩個(gè)泛型 T
和 R
。其中 createAutoMergedRequest
接受的 fn
參數(shù)的類型是 (mergedParams: T[]) => Promise<R[]>
,而返回的函數(shù)定義是 (params: T): Promise<R>
。這是因?yàn)樗麜?huì)自動(dòng)把請(qǐng)求的結(jié)果拆分成獨(dú)立的返回值返回到對(duì)應(yīng)的調(diào)用處。
我們看返回的函數(shù)體:
if (!timer) { // 如果沒有開始窗口期,則創(chuàng)建 timer = window.setTimeout(() => { submitQueue(); }, windowMs); } return new Promise<R>((resolve, reject) => { queue.push({ params, resolve, reject, }); });
首先判斷閉包中是否存在定時(shí)器 timer, 如果沒有則創(chuàng)建一個(gè)timer,在 windowMs
后執(zhí)行 submitQueue
方法。我們把 windowMs
定義為窗口期,在這個(gè)窗口期內(nèi)調(diào)用該函數(shù)的請(qǐng)求都會(huì)被收集起來。
然后創(chuàng)建返回一個(gè)promise,把參數(shù)和promise相關(guān)的下一步操作都推到 queue
中。
等到若干次調(diào)用后,定時(shí)器到時(shí)間了,喚起回調(diào)執(zhí)行submitQueue
方法,我們來看看 submitQueue
的操作。
async function submitQueue() { timer = null; // 清空計(jì)時(shí)器以接受后續(xù)請(qǐng)求 const _queue = [...queue]; queue = []; // 清空隊(duì)列 const ret = fn(_queue.map((q) => q.params)); try { const list = await fn(_queue.map((q) => q.params)); _queue.forEach((q1, i) => { q1.resolve(list[i]); }); } catch (err) { _queue.forEach((q2) => { q2.reject(err); }); } }
執(zhí)行前我們會(huì)做一些前置工作,清理 timer
, 清理 queue
并把隊(duì)列里的項(xiàng)單獨(dú)存放起來,防止影響到下一次執(zhí)行。
然后我們通過 fn(_queue.map((q) => q.params))
來把隊(duì)列中的參數(shù)拿出來,傳給 fn
調(diào)用。此時(shí)的 fn
就會(huì)接收到一個(gè)數(shù)組。并確保返回的結(jié)果也是一個(gè)同等大小且一一對(duì)應(yīng)的數(shù)據(jù)即可。如果請(qǐng)求無誤,我們就循環(huán)隊(duì)列,把結(jié)果通過隊(duì)列中記錄的 resolve
把結(jié)果返回給我們之前創(chuàng)建的promise。
這樣我們就實(shí)現(xiàn)了一個(gè)工具函數(shù),我們可以在一個(gè)窗口期內(nèi)收集到多個(gè)網(wǎng)絡(luò)請(qǐng)求,并把他們匯聚成一個(gè)請(qǐng)求發(fā)送到后端。后端結(jié)果返回回來后,我們?cè)侔颜?qǐng)求結(jié)果拆分分發(fā)給獨(dú)立的調(diào)用方。
?
文章來源:http://www.zghlxwxcb.cn/news/detail-633083.html
本文轉(zhuǎn)載于:
https://juejin.cn/post/7259275893796388925
如果對(duì)您有所幫助,歡迎您點(diǎn)個(gè)關(guān)注,我會(huì)定時(shí)更新技術(shù)文檔,大家一起討論學(xué)習(xí),一起進(jìn)步。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-633083.html
到了這里,關(guān)于記錄--前端實(shí)用小技巧: 自動(dòng)合并的網(wǎng)絡(luò)請(qǐng)求的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!