最近在進行移動端h5開發,首頁需要加載的資源很多,一個lottie動效需要請求70多張圖片,但是遇到安卓webview限制請求并發數,導致部分圖片請求失敗破圖。當然圖片資源可以做閑時加載和預加載,可以減輕播放動效時資源未加載的問題。
上面代碼基本實現了前端并發請求的需求,也基本滿足需求,在生產中其實有很多已經封裝好的庫可以直接使用。比如:p-limit【https://github.com/sindresorhus/p-limit】
import Queue from 'yocto-queue';import {AsyncResource} from '#async_hooks';export default function pLimit(concurrency) { // 判斷這個參數是否是一個大于0的整數,如果不是就拋出一個錯誤 if ( !((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0) ) { throw new TypeError('Expected `concurrency` to be a number from 1 and up'); } // 創建隊列 -- 用于存取請求 const queue = new Queue(); // 計數 let activeCount = 0; // 用來處理并發數的函數 const next = () => { activeCount--; if (queue.size > 0) { // queue.dequeue()可以理解為[].shift(),取出隊列中的第一個任務,由于確定里面是一個函數,所以直接執行就可以了; queue.dequeue()(); } }; // run函數就是用來執行異步并發任務 const run = async (function_, resolve, arguments_) => { // activeCount加1,表示當前并發數加1 activeCount++; // 執行傳入的異步函數,將結果賦值給result,注意:現在的result是一個處在pending狀態的Promise const result = (async () => function_(...arguments_))(); // resolve函數就是enqueue函數中返回的Promise的resolve函數 resolve(result); // 等待result的狀態發生改變,這里使用了try...catch,因為result可能會出現異常,所以需要捕獲異常; try { await result; } catch {} next(); }; // 將run函數添加到請求隊列中 const enqueue = (function_, resolve, arguments_) => { queue.enqueue( // 將run函數綁定到AsyncResource上,不需要立即執行,對此添加了一個bind方法 AsyncResource.bind(run.bind(undefined, function_, resolve, arguments_)), ); // 立即執行一個異步函數,等待下一個微任務(注意:因為activeCount是異步更新的,所以需要等待下一個微任務執行才能獲取新的值) (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,并且隊列中有任務,如果滿足條件就會將隊列中的任務取出來執行 if (activeCount < concurrency && queue.size > 0) { // 注意:queue.dequeue()()執行的是run函數 queue.dequeue()(); } })(); }; // 接收一個函數fn和參數args,然后返回一個Promise,執行出隊操作 const generator = (function_, ...arguments_) => new Promise(resolve => { enqueue(function_, resolve, arguments_); }); // 向外暴露當前的并發數和隊列中的任務數,并且手動清空隊列 Object.defineProperties(generator, { // 當前并發數 activeCount: { get: () => activeCount, }, // 隊列中的任務數 pendingCount: { get: () => queue.size, }, // 清空隊列 clearQueue: { value() { queue.clear(); }, }, }); return generator;}
整個庫只有短短71行代碼,在代碼中導入了yocto-queue庫,它是一個微型的隊列數據結構。
在進行手撕源碼時,可以借助數組進行簡易的實現:
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()(); } }}
在這篇文章中,簡要介紹了為什么要進行并發請求,闡述了使用請求池隊列實現并發請求的設計思路,簡要實現代碼。
此外,還閱讀分析了p-limit的源碼,并使用數組進行簡要的源碼編寫,以實現要求。
本文鏈接:http://www.tebozhan.com/showinfo-26-87046-0.html前端開發中大并發量如何控制并發數
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 如何優雅的變更Docker Desktop的鏡像存儲路徑
下一篇: 盤點Lombok的幾個操作,你記住了嗎?