描述如下
我們要同時(shí)發(fā)多個(gè)相同的請(qǐng)求,第一個(gè)請(qǐng)求成功后,剩余結(jié)果都不會(huì)發(fā)出,返回結(jié)果是成果。
假如第一個(gè)反饋失敗,第二個(gè)是成功,后面就不會(huì)發(fā)出,后面都直接反饋成功。第三個(gè)才是成功的話,后面就不會(huì)在發(fā)出,后面都反饋成功。依次如此處理,直至最后一個(gè)。
并發(fā): 一個(gè)接口請(qǐng)求還處于pending,短時(shí)間內(nèi)就發(fā)送相同的請(qǐng)求
async function fetchData (a) { const data = await fetch('//127.0.0.1:3000/test') const d = await data.json(); console.log(d); return d; } fetchData(2) // 編號(hào) 1 fetchData(2) // 2 fetchData(2) // 3 fetchData(2) // 4 fetchData(2) // 4 fetchData(2) // 5 fetchData(2) fetchData(2)
老版本cachedAsync
之前講過vue的緩存函數(shù)緩存成功的請(qǐng)求, 實(shí)現(xiàn)是這樣的。現(xiàn)在來說說cachedAsync只會(huì)緩存成功的請(qǐng)求,但假如失敗了,只有直接拉起新的請(qǐng)求。但是如果是上面的并發(fā)場景,相同的請(qǐng)求因?yàn)闊o法命中緩存,會(huì)出現(xiàn)連續(xù)發(fā)送三個(gè)請(qǐng)求的問題,無法處理這種并發(fā)的場景。
const cachedAsync = function(fn) { const cache = Object.create(null); return async str => { const hit = cache[str]; if (hit) { return hit; } // 只緩存成功的Promise, 失敗直接重新請(qǐng)求 return (cache[str] = await fn(str)); }; }; const fetch2 = cachedAsync(fetchData) fetch2(2); fetch2(2); fetch2(2);
進(jìn)階版本
我們要知道緩存是必須的,因此我們只要處理怎么控制并發(fā)即可。這一個(gè)解決思路。
每個(gè)請(qǐng)求都返回一個(gè)新的Promise, Promise的exector的執(zhí)行時(shí)機(jī),通過一個(gè)隊(duì)列保存。
當(dāng)隊(duì)列長度為1的時(shí)候,執(zhí)行一次請(qǐng)求,如果請(qǐng)求成功,那么遍歷隊(duì)列中的exector,拿到請(qǐng)求的結(jié)果然后resolve。
如果請(qǐng)求失敗了,那么就把這個(gè)Promise reject掉,同時(shí)出棧。然后遞歸調(diào)用next
直到exector隊(duì)列清空為止
const cacheAsync = (promiseGenerator, symbol) => { const cache = new Map(); const never = Symbol(); return async (params) => { return new Promise((resolve, reject) => { // 可以提供鍵值 symbol = symbol || params; let cacheCfg = cache.get(symbol); if (!cacheCfg) { cacheCfg = { hit: never, exector: [{ resolve, reject }], }; cache.set(symbol, cacheCfg); } else { // 命中緩存 if (cacheCfg.hit !== never) { return resolve(cacheCfg.hit) } cacheCfg.exector.push({ resolve, reject }); } const { exector } = cacheCfg; // 處理并發(fā),在請(qǐng)求還處于pending過程中就發(fā)起了相同的請(qǐng)求 // 拿第一個(gè)請(qǐng)求 if (exector.length === 1) { const next = async () => { try { if (!exector.length) return; const response = await promiseGenerator(params); // 如果成功了,那么直接resolve掉剩余同樣的請(qǐng)求 while (exector.length) { // 清空 exector.shift().resolve(response); } // 緩存結(jié)果 cacheCfg.hit = response; } catch (error) { // 如果失敗了 那么這個(gè)promise的則為reject const { reject } = exector.shift(); reject(error); next(); // 失敗重試,降級(jí)為串行 } }; next(); } }); }; };
測試cacheAsync
現(xiàn)在需要測試的場景,測試請(qǐng)求接口隨機(jī)出現(xiàn)成功或者失敗,假如成功預(yù)期結(jié)果,剩余的請(qǐng)求都不會(huì)發(fā)出,這樣失敗重試,接著發(fā)下一個(gè)請(qǐng)求。
現(xiàn)在我們先快速搭建一個(gè)服務(wù)器
const koa = require("koa"); const app = new koa(); function sleep(seconds) { return new Promise((resolve, reject) => { setTimeout(resolve, seconds); }); } app.use(async (ctx, next) => { if (ctx.url === "/test") { await sleep(200); const n = Math.random(); // 隨機(jī)掛掉接口 if (n > 0.8) { ctx.body = n; } else { ctx.status = 404 ctx.body = '' } next(); } }); app.listen(3000, "127.0.0.1", () => console.log("listening on 127.0.0.1:3000") );
客戶端
var fetch2 = cacheAsync(fetchData, "test2"); async function fetchData(a) { const data = await fetch("//127.0.0.1:3000/test"); const d = await data.json(); console.log(d); return d; } // 并發(fā)6個(gè)相同的請(qǐng)求 console.log(fetch2(2)); console.log(fetch2(2)); console.log(fetch2(2)); console.log(fetch2(2)); console.log(fetch2(2)); console.log(fetch2(2));
看下測試結(jié)果,刷新下頁面
第一次運(yùn)氣很好,第一次接口就請(qǐng)求成功,只發(fā)送了一個(gè)請(qǐng)求
第二次測試運(yùn)氣不好,最后一個(gè)請(qǐng)求才成功,也是最差的場景
第三次測試,請(qǐng)求第三次成功了
測試下緩存在控制臺(tái)主動(dòng)請(qǐng)求fetch2,成功命中。
上面表示從測試結(jié)果來看是正確的,符合了并發(fā)和緩存的場景。但是為什么要緩存接口。簡單來說就是,當(dāng)輸入關(guān)鍵字搜索,監(jiān)聽的是input事件,在你增刪關(guān)鍵字的時(shí)候,就會(huì)出現(xiàn)請(qǐng)求參數(shù)一樣的情況,因此就符合防抖+前端接口緩存的方式。遇到相同關(guān)鍵字直接拉之前的緩存。
提示
這個(gè)緩存因?yàn)槭情]包的方式,因此刷新頁面緩存也失效了。不過我認(rèn)為這個(gè)是理應(yīng)如此,因?yàn)榇蟛糠謭鼍八⑿马撁?,就是要重置狀態(tài),如果要持久化,還不如保存到本地存儲(chǔ)。
github-demo
歡迎大家繼續(xù)關(guān)注后續(xù)更多精彩內(nèi)容。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/128013.html
摘要:事務(wù)隔離級(jí)別定義了一個(gè)事務(wù)可能受其他并發(fā)事務(wù)影響的程度我們先來看一下并發(fā)事務(wù)帶來的問題,然后再來介紹一下接口中定義了五個(gè)表示隔離級(jí)別的常量。 Java面試通關(guān)手冊(Java學(xué)習(xí)指南):https://github.com/Snailclimb/Java_Guide 微信閱讀地址鏈接:可能是最漂亮的Spring事務(wù)管理詳解 事務(wù)概念回顧 什么是事務(wù)? 事務(wù)是邏輯上的一組操作,要么都執(zhí)行,...
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里什么是原型鏈當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法時(shí)候就會(huì)產(chǎn)生一個(gè)原型鏈。函數(shù)式編程是聲明式而不是命令式,并且應(yīng)用程序狀態(tài)通過純函數(shù)流轉(zhuǎn)。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.什么是原型鏈 當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方...
摘要:渲染引擎也稱為呈現(xiàn)引擎瀏覽器內(nèi)核,負(fù)責(zé)顯示請(qǐng)求的內(nèi)容。引擎是基于事件驅(qū)動(dòng)單線程執(zhí)行的,引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來,然后加以處理,瀏覽器無論什么時(shí)候都只有一個(gè)線程在運(yùn)行程序。 1 瀏覽器結(jié)構(gòu) showImg(https://segmentfault.com/img/bVk7AU); 瀏覽器分為以下7個(gè)部分: 用戶界面 瀏覽器引擎:在用戶界面和呈現(xiàn)引擎之間傳送指令。 渲染引擎:也...
閱讀 566·2023-03-27 18:33
閱讀 755·2023-03-26 17:27
閱讀 656·2023-03-26 17:14
閱讀 608·2023-03-17 21:13
閱讀 541·2023-03-17 08:28
閱讀 1829·2023-02-27 22:32
閱讀 1324·2023-02-27 22:27
閱讀 2207·2023-01-20 08:28