最近在進(jìn)行移動(dòng)端h5開發(fā),首頁需要加載的資源很多,一個(gè)lottie動(dòng)效需要請(qǐng)求70多張圖片,但是遇到安卓webview限制請(qǐng)求并發(fā)數(shù),導(dǎo)致部分圖片請(qǐng)求失敗破圖。當(dāng)然圖片資源可以做閑時(shí)加載和預(yù)加載,可以減輕播放動(dòng)效時(shí)資源未加載的問題。
上面代碼基本實(shí)現(xiàn)了前端并發(fā)請(qǐng)求的需求,也基本滿足需求,在生產(chǎn)中其實(shí)有很多已經(jīng)封裝好的庫可以直接使用。比如:p-limit【https://github.com/sindresorhus/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; // 用來處理并發(fā)數(shù)的函數(shù) const next = () => { activeCount--; if (queue.size > 0) { // queue.dequeue()可以理解為[].shift(),取出隊(duì)列中的第一個(gè)任務(wù),由于確定里面是一個(gè)函數(shù),所以直接執(zhí)行就可以了; queue.dequeue()(); } }; // run函數(shù)就是用來執(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ù)取出來執(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è)庫只有短短71行代碼,在代碼中導(dǎo)入了yocto-queue庫,它是一個(gè)微型的隊(duì)列數(shù)據(jù)結(jié)構(gòu)。
在進(jìn)行手撕源碼時(shí),可以借助數(shù)組進(jì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()(); } }}
在這篇文章中,簡要介紹了為什么要進(jìn)行并發(fā)請(qǐng)求,闡述了使用請(qǐng)求池隊(duì)列實(shí)現(xiàn)并發(fā)請(qǐng)求的設(shè)計(jì)思路,簡要實(shí)現(xiàn)代碼。
此外,還閱讀分析了p-limit的源碼,并使用數(shù)組進(jìn)行簡要的源碼編寫,以實(shí)現(xiàn)要求。
本文鏈接:http://www.tebozhan.com/showinfo-26-87046-0.html前端開發(fā)中大并發(fā)量如何控制并發(fā)數(shù)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com