成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

詳解JS前端并發(fā)多個(gè)相同的請(qǐng)求控制為只發(fā)一個(gè)請(qǐng)求方式

3403771864 / 1130人閱讀

  描述如下

  我們要同時(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)求

1.png

  第二次測試運(yùn)氣不好,最后一個(gè)請(qǐng)求才成功,也是最差的場景

2.png

  第三次測試,請(qǐng)求第三次成功了

3.png

  測試下緩存在控制臺(tái)主動(dòng)請(qǐng)求fetch2,成功命中。

4.png

  上面表示從測試結(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

相關(guān)文章

  • 可能是最漂亮Spring事務(wù)管理詳解

    摘要:事務(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í)行,...

    鄒立鵬 評(píng)論0 收藏0
  • 前端基本功-常見概念(一)

    摘要:前端基本功常見概念一點(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è)引用類型的屬性和方...

    bladefury 評(píng)論0 收藏0
  • 瀏覽器詳解

    摘要:渲染引擎也稱為呈現(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)引擎之間傳送指令。 渲染引擎:也...

    Amos 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<