国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

前端開發(fā)中大并發(fā)量如何控制并發(fā)數(shù)

開發(fā) 前端
在這篇文章中,簡(jiǎn)要介紹了為什么要進(jìn)行并發(fā)請(qǐng)求,闡述了使用請(qǐng)求池隊(duì)列實(shí)現(xiàn)并發(fā)請(qǐng)求的設(shè)計(jì)思路,簡(jiǎn)要實(shí)現(xiàn)代碼。此外,還閱讀分析了p-limit的源碼,并使用數(shù)組進(jìn)行簡(jiǎn)要的源碼編寫,以實(shí)現(xiàn)要求。

寫在前面

最近在進(jìn)行移動(dòng)端h5開發(fā),首頁(yè)需要加載的資源很多,一個(gè)lottie動(dòng)效需要請(qǐng)求70多張圖片,但是遇到安卓webview限制請(qǐng)求并發(fā)數(shù),導(dǎo)致部分圖片請(qǐng)求失敗破圖。當(dāng)然圖片資源可以做閑時(shí)加載和預(yù)加載,可以減輕播放動(dòng)效時(shí)資源未加載的問(wèn)題。

同樣的,業(yè)務(wù)開發(fā)也會(huì)遇到需要異步請(qǐng)求幾十個(gè)接口,如果同時(shí)并發(fā)請(qǐng)求瀏覽器會(huì)進(jìn)行限制請(qǐng)求數(shù),也會(huì)給后端造成請(qǐng)求壓力。

場(chǎng)景說(shuō)明

現(xiàn)在有個(gè)場(chǎng)景:

請(qǐng)你實(shí)現(xiàn)一個(gè)并發(fā)請(qǐng)求函數(shù)concurrencyRequest(urls, maxNum),要求如下:

  • 要求最大并發(fā)數(shù) maxNum。
  • 每當(dāng)有一個(gè)請(qǐng)求返回,就留下一個(gè)空位,可以增加新的請(qǐng)求。
  • 所有請(qǐng)求完成后,結(jié)果按照 urls 里面的順序依次打出(發(fā)送請(qǐng)求的函數(shù)可以直接使用fetch即可)。

初始實(shí)現(xiàn):

const preloadManger = (urls, maxCount = 5) => {
  let count = 0; // 計(jì)數(shù) -- 用于控制并發(fā)數(shù)
  const createTask = () => {
    if (count < maxCount) {
      const url = urls.pop(); // 從請(qǐng)求數(shù)組中取值
      if (url) {
        // 無(wú)論請(qǐng)求是否成功,都要執(zhí)行taskFinish
        loader(url).finally(taskFinish);
        // 添加下一個(gè)請(qǐng)求
        count++;
        createTask();
      }
    }
  };

  const taskFinish = () => {
    count--;
    createTask();
  };

  createTask();
};

// 進(jìn)行異步請(qǐng)求
const loader = async (url) => {
  const res = await fetch(url).then(res=>res.json());
  console.log("res",res);
  return res
}

const urls = [];
for (let i = 1; i <= 20; i++) {
    urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}

preloadManger(urls, 5)

請(qǐng)求狀態(tài):

可以看到上面的請(qǐng)求是每五個(gè)一組進(jìn)行請(qǐng)求,當(dāng)一個(gè)請(qǐng)求無(wú)論返回成功或是失敗,都會(huì)從請(qǐng)求數(shù)組中再取一個(gè)請(qǐng)求進(jìn)行補(bǔ)充。

設(shè)計(jì)思路

那么,我們可以考慮使用隊(duì)列去請(qǐng)求大量接口。

思路如下:

假定最大并發(fā)數(shù)是maxNum=5,圖中對(duì)接口進(jìn)行了定義編號(hào),當(dāng)請(qǐng)求隊(duì)列池中有一個(gè)請(qǐng)求返回后,就向池子中新增一個(gè)接口進(jìn)行請(qǐng)求,依次直到最后一個(gè)請(qǐng)求執(zhí)行完畢。

當(dāng)然,要保證程序的健壯性,需要考慮一些邊界情況,如下:

  • 當(dāng)初始請(qǐng)求數(shù)組urls的長(zhǎng)度為0時(shí),此時(shí)請(qǐng)求結(jié)果數(shù)組results是個(gè)空數(shù)組。
  • 最大并發(fā)數(shù)maxNums>urls的長(zhǎng)度時(shí),請(qǐng)求數(shù)為urls的長(zhǎng)度。
  • 需要定義計(jì)數(shù)器count去判斷是否全部請(qǐng)求完畢。
  • 無(wú)論請(qǐng)求成功與否,都應(yīng)該將結(jié)果存在結(jié)果數(shù)組results中。
  • 結(jié)果數(shù)組results和urls數(shù)組的順序保持一致,方便存取。

代碼實(shí)現(xiàn)

在前面的初始實(shí)現(xiàn)的代碼中,雖然都能滿足基本需求,但是并沒有考慮一些邊界條件,對(duì)此需要根據(jù)上面設(shè)計(jì)思路重新實(shí)現(xiàn)得到:

// 并發(fā)請(qǐng)求函數(shù)
const concurrencyRequest = (urls, maxNum) => {
    return new Promise((resolve) => {
        if (urls.length === 0) {
            resolve([]);
            return;
        }
        const results = [];
        let index = 0; // 下一個(gè)請(qǐng)求的下標(biāo)
        let count = 0; // 當(dāng)前請(qǐng)求完成的數(shù)量

        // 發(fā)送請(qǐng)求
        async function request() {
            if (index === urls.length) return;
            const i = index; // 保存序號(hào),使result和urls相對(duì)應(yīng)
            const url = urls[index];
            index++;
            console.log(url);
            try {
                const resp = await fetch(url);
                // resp 加入到results
                results[i] = resp;
            } catch (err) {
                // err 加入到results
                results[i] = err;
            } finally {
                count++;
                // 判斷是否所有的請(qǐng)求都已完成
                if (count === urls.length) {
                    console.log('完成了');
                    resolve(results);
                }
                request();
            }
        }

        // maxNum和urls.length取最小進(jìn)行調(diào)用
        const times = Math.min(maxNum, urls.length);
        for(let i = 0; i < times; i++) {
            request();
        }
    })
}

測(cè)試代碼:

const urls = [];
for (let i = 1; i <= 20; i++) {
    urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}
concurrencyRequest(urls, 5).then(res => {
    console.log(res);
})

請(qǐng)求結(jié)果:

上面代碼基本實(shí)現(xiàn)了前端并發(fā)請(qǐng)求的需求,也基本滿足需求,在生產(chǎn)中其實(shí)有很多已經(jīng)封裝好的庫(kù)可以直接使用。比如:p-limit【https://github.com/sindresorhus/p-limit】

閱讀p-limit源碼

import Queue from 'yocto-queue';
import {AsyncResource} from '#async_hooks';

export default function pLimit(concurrency) {
 // 判斷這個(gè)參數(shù)是否是一個(gè)大于0的整數(shù),如果不是就拋出一個(gè)錯(cuò)誤
 if (
  !((Number.isInteger(concurrency)
  || concurrency === Number.POSITIVE_INFINITY)
  && concurrency > 0)
 ) {
  throw new TypeError('Expected `concurrency` to be a number from 1 and up');
 }

 // 創(chuàng)建隊(duì)列 -- 用于存取請(qǐng)求
 const queue = new Queue();
 // 計(jì)數(shù)
 let activeCount = 0;

 // 用來(lái)處理并發(fā)數(shù)的函數(shù)
 const next = () => {
  activeCount--;

  if (queue.size > 0) {
   // queue.dequeue()可以理解為[].shift(),取出隊(duì)列中的第一個(gè)任務(wù),由于確定里面是一個(gè)函數(shù),所以直接執(zhí)行就可以了;
   queue.dequeue()();
  }
 };

 // run函數(shù)就是用來(lái)執(zhí)行異步并發(fā)任務(wù)
 const run = async (function_, resolve, arguments_) => {
  // activeCount加1,表示當(dāng)前并發(fā)數(shù)加1
  activeCount++;

  // 執(zhí)行傳入的異步函數(shù),將結(jié)果賦值給result,注意:現(xiàn)在的result是一個(gè)處在pending狀態(tài)的Promise
  const result = (async () => function_(...arguments_))();

  // resolve函數(shù)就是enqueue函數(shù)中返回的Promise的resolve函數(shù)
  resolve(result);

  // 等待result的狀態(tài)發(fā)生改變,這里使用了try...catch,因?yàn)閞esult可能會(huì)出現(xiàn)異常,所以需要捕獲異常;
  try {
   await result;
  } catch {}

  next();
 };

 // 將run函數(shù)添加到請(qǐng)求隊(duì)列中
 const enqueue = (function_, resolve, arguments_) => {
  queue.enqueue(
   // 將run函數(shù)綁定到AsyncResource上,不需要立即執(zhí)行,對(duì)此添加了一個(gè)bind方法
   AsyncResource.bind(run.bind(undefined, function_, resolve, arguments_)),
  );

  // 立即執(zhí)行一個(gè)異步函數(shù),等待下一個(gè)微任務(wù)(注意:因?yàn)閍ctiveCount是異步更新的,所以需要等待下一個(gè)微任務(wù)執(zhí)行才能獲取新的值)
  (async () => {
   // This function needs to wait until the next microtask before comparing
   // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
   // when the run function is dequeued and called. The comparison in the if-statement
   // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
   await Promise.resolve();

   // 判斷activeCount是否小于concurrency,并且隊(duì)列中有任務(wù),如果滿足條件就會(huì)將隊(duì)列中的任務(wù)取出來(lái)執(zhí)行
   if (activeCount < concurrency && queue.size > 0) {
    // 注意:queue.dequeue()()執(zhí)行的是run函數(shù)
    queue.dequeue()();
   }
  })();
 };

 // 接收一個(gè)函數(shù)fn和參數(shù)args,然后返回一個(gè)Promise,執(zhí)行出隊(duì)操作
 const generator = (function_, ...arguments_) => new Promise(resolve => {
  enqueue(function_, resolve, arguments_);
 });

 // 向外暴露當(dāng)前的并發(fā)數(shù)和隊(duì)列中的任務(wù)數(shù),并且手動(dòng)清空隊(duì)列
 Object.defineProperties(generator, {
  // 當(dāng)前并發(fā)數(shù)
  activeCount: {
   get: () => activeCount,
  },
  // 隊(duì)列中的任務(wù)數(shù)
  pendingCount: {
   get: () => queue.size,
  },
  // 清空隊(duì)列
  clearQueue: {
   value() {
    queue.clear();
   },
  },
 });

 return generator;
}

整個(gè)庫(kù)只有短短71行代碼,在代碼中導(dǎo)入了yocto-queue庫(kù),它是一個(gè)微型的隊(duì)列數(shù)據(jù)結(jié)構(gòu)。

手寫源碼

在進(jìn)行手撕源碼時(shí),可以借助數(shù)組進(jìn)行簡(jiǎn)易的實(shí)現(xiàn):

class PLimit {
    constructor(concurrency) {
        this.concurrency = concurrency;
        this.activeCount = 0;
        this.queue = [];
        
        return (fn, ...args) => {
            return new Promise(resolve => {
               this.enqueue(fn, resolve, args);
            });
        }
    }
    
    enqueue(fn, resolve, args) {
        this.queue.push(this.run.bind(this, fn, resolve, args));

        (async () => {
            await Promise.resolve();
            if (this.activeCount < this.concurrency && this.queue.length > 0) {
                this.queue.shift()();
            }
        })();
    }
    
    async run(fn, resolve, args) {
        this.activeCount++;

        const result = (async () => fn(...args))();

        resolve(result);

        try {
            await result;
        } catch {
        }

        this.next();
    }
    
    next() {
        this.activeCount--;

        if (this.queue.length > 0) {
            this.queue.shift()();
        }
    }
}

小結(jié)

在這篇文章中,簡(jiǎn)要介紹了為什么要進(jìn)行并發(fā)請(qǐng)求,闡述了使用請(qǐng)求池隊(duì)列實(shí)現(xiàn)并發(fā)請(qǐng)求的設(shè)計(jì)思路,簡(jiǎn)要實(shí)現(xiàn)代碼。

此外,還閱讀分析了p-limit的源碼,并使用數(shù)組進(jìn)行簡(jiǎn)要的源碼編寫,以實(shí)現(xiàn)要求。

參考文章

  • 【源碼共讀】大并發(fā)量如何控制并發(fā)數(shù)https://juejin.cn/post/7179220832575717435?searchId=20240430092814392DC2208C545E691A26
  • 前端實(shí)現(xiàn)并發(fā)控制網(wǎng)絡(luò)請(qǐng)求https://mp.weixin.qq.com/s/9uq2SqkcMSSWjks0x7RQJg。
責(zé)任編輯:姜華 來(lái)源: 宇宙一碼平川
相關(guān)推薦

2017-08-21 10:56:55

MySQL并發(fā)控制

2021-04-07 06:00:18

JavaScript 前端并發(fā)控制

2021-01-12 10:22:45

JavaScript并發(fā)控制前端

2024-06-17 08:40:16

2009-09-24 14:43:53

Hibernate樂(lè)觀

2022-12-12 09:07:06

Redis并發(fā)限流

2009-06-09 09:32:48

LinuxApache帶寬控制

2023-11-20 08:01:38

并發(fā)處理數(shù)Tomcat

2021-06-29 23:40:19

Golang語(yǔ)言并發(fā)

2010-11-08 10:57:05

SQL Server的

2021-02-25 22:17:19

開發(fā)技術(shù)編程

2009-11-25 11:41:56

IIS最大并發(fā)數(shù)

2020-01-13 10:20:30

架構(gòu)聊天架構(gòu)百萬(wàn)并發(fā)量

2017-11-06 17:16:55

Linux設(shè)備驅(qū)動(dòng)并發(fā)控制

2023-01-30 15:41:10

Channel控制并發(fā)

2024-08-26 13:23:26

2025-02-28 00:03:22

高并發(fā)TPS系統(tǒng)

2025-02-26 03:00:00

2017-02-28 17:46:15

Linux驅(qū)動(dòng)技術(shù)并發(fā)控制

2009-02-09 10:06:03

并發(fā)控制Web應(yīng)用悲觀鎖
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

成人免费高清观看| av一二三不卡影片| 成人综合在线观看| 精品久久久久久最新网址| 日本精品视频网站| 麻豆免费网站| 日本一区影院| 亚洲国产精品v| 日韩最新免费不卡| 日韩午夜视频在线观看| 久草在线资源视频在线观看| 久久国产影院| 六十路在线观看| 在线观看免费国产小视频| 麻豆精品网站| 综合网中文字幕| 欧美日韩影视| 国产欧美一区二区三区鸳鸯浴| 国产精品视频网站| 香蕉久久久久久| 五月天久久比比资源色| 激情五月宗合网| 国产亚洲亚洲| 成人国产精品日本在线| 99精品国产高清一区二区麻豆| 亚洲国产欧美一区二区三区同亚洲 | 小小水蜜桃在线观看| 在线午夜精品自拍| 国产一区二区在线网站 | 日韩在线观看免费高清| 欧美高清视频一区| 牛牛电影国产一区二区| 成人免费视频视频在线观看免费 | 麻豆视频成人| 欧美视频1区| 国产黄色片在线播放| 风间由美一区二区av101| 91视频免费播放| 蜜臀久久99精品久久久无需会员| 视色,视色影院,视色影库,视色网| 久久av偷拍| 尤物九九久久国产精品的特点 | 国产免费福利网站| 日本色综合中文字幕| 久久久一本精品99久久精品66| 最新中文字幕免费视频| 国产欧美一区二区精品久导航| 永久免费在线| 美国成人xxx| 精品国产一区二区三区久久久蜜月 | 色屁屁一区二区| 欧美日韩精品久久| 久久99精品久久久久久国产越南| 日韩免费av电影| 日韩精品四区| 国产精品扒开腿做爽爽爽的视频| 性xxxfreexxxx性欧美| 欧美经典一区二区三区| 免费成人av电影| 91免费视频网站在线观看| 免费看男女www网站入口在线| 色先锋资源久久综合5566| japanese国产精品| 97视频在线免费播放| 亚洲国语精品自产拍在线观看| 18国产精品| 精品欧美一区免费观看α√| 精品国产欧美一区二区五十路 | 澳门久久精品| 亚洲一区bb| 欧美一级二级三级蜜桃| 亚洲日本va| 日本一区二区在线| 午夜在线成人av| 国语一区二区三区| 一二三四视频社区在线| 欧美一级高清大全免费观看| 色天下一区二区三区| 成年人视频观看| 国内精品视频666| 无码播放一区二区三区| 欧美老女人在线视频| 国产一区二区在线观| 成人综合电影| 日韩你懂的电影在线观看| 久久国产精品区| 国产精品3区| 免费观看黄色网| 国产精品国色综合久久| 日韩免费看网站| 91网站在线观看视频| 国产精品久久久久久影院8一贰佰| www.成人av.com| 亚洲精品色图| 91亚洲va在线va天堂va国 | 另类亚洲自拍| 成人在线视频一区二区三区| 日韩欧美精品网址| 黄网页免费在线观看| 精品福利一区二区三区 | 日韩欧美大片| 亚洲人成无码网站久久99热国产| 欧美日韩国产精品一区二区不卡中文| 精品中文在线| 成人h在线播放| 99国产精品视频免费观看| 性网站在线看| 成人在线视频网| 99久久久无码国产精品性色戒| 亚洲午夜小视频| 国产福利91精品一区二区三区| 日韩一区二区三区四区五区六区| a成人v在线| 欧美激情伊人电影| 激情图片小说一区| 欧美成人明星100排名| 成人97在线观看视频| 韩国理伦片一区二区三区在线播放| 日本韩国精品一区二区| 国产 高清 精品 在线 a| 亚洲v精品v日韩v欧美v专区| 国产精品国产亚洲精品| 少妇无码av无码专区在线观看 | 91精品久久久久久| 欧美国产精品一区二区| 成人日韩精品| 国产二区视频在线播放| zzjj国产精品一区二区| 视频一区中文字幕| 偷拍精品精品一区二区三区| 欧美日韩国产不卡| 天天射综合网视频| 成人黄色影视| 日本久久久久久久久| 欧美黑人xx片| 蜜臀av性久久久久蜜臀av| 亚洲精品美女久久久| 91麻豆免费在线观看| 欧美**字幕| av影片在线| 超污网站在线观看| 午夜视频你懂的| 国产高清免费在线| 日韩av视屏| 91av在线国产| 亚洲国产一区二区三区青草影视| 蜜臀av一区| 李宗瑞系列合集久久| 亚洲综合中文字幕在线观看| 日本乱人伦一区| 国产日韩欧美| 精品丝袜在线| 国产午夜福利100集发布| 在线性视频日韩欧美| 色综合天天综合| 国产成人午夜精品影院观看视频| 青青草原在线亚洲| 桃色一区二区| 九义人在线观看完整免费版电视剧| 一区二区三区在线视频看| 91sa在线看| 91情侣偷在线精品国产| 成人国产精品免费观看视频| 男人亚洲天堂| 中文字幕国产免费| 国产精品久久久久99| 色多多在线观看| 成年人视频在线观看免费| 四虎最新地址发布| 亚洲第一视频| 成人午夜电影在线播放| 91精品久久久久久蜜臀| 日本欧美一区二区在线观看| 日韩免费福利视频| 成人免费网址在线| 国产免费一区二区三区| 国产性猛交xxxx免费看久久| 国产网红主播福利一区二区| 五月久久久综合一区二区小说| 黄色在线看片| 国产成人无码av在线播放dvd| 国产美女直播视频一区| 日韩欧美在线网站| 欧美国产精品劲爆| 99视频一区| 欧一区二区三区| av在线播放网站| 国产精品无码专区av在线播放| 亚洲最大av网站| 亚洲色图50p| 亚洲高清免费在线| 国产一区二区三区高清播放| 国产亚洲一卡2卡3卡4卡新区 | wwwxx欧美| 久久久久999| 色域天天综合网| 成人福利视频在线看| 亚洲国产二区| 亚洲丁香日韩| 激情久久一区二区|