在我們平時開發中,經常會遇到頁面資料初始化時,頻繁調同一個介面的情況,比如echarts專案中,一個頁面可能會有幾十張圖表,如果一個介面回傳所有圖表資料的話,會造成用戶過長的等待時間,再者過多圖表同時渲染,也會給頁面增加壓力,造成卡頓的現象,
我們通常會讓每個圖表單獨調一個介面,入參不同,這樣更有利于頁面快速渲染圖表,單個圖表請求到資料,立即渲染,不需要等待其他圖表,可理想很豐滿,現實很骨感,當服務器配置過低,或者后端代碼性能較弱,會難以處理這些并發請求,介面呼叫越多,等待處理的時間可能就越長,甚至超過一次性回傳所有資料的間,,,為了解決這種問題,緩解后端壓力,本篇將介紹前端來控制請求的并發數:
先分析一波,假設我們需要重復呼叫30次介面,并聯呼叫介面,服務端壓力較大,可能會造成回應時間過長,逐漸減少并發數,假設并發數為5的時候,服務器處理速度最快,幾乎不受并發影響,
針對這種情況,我們可以封裝介面請求方法,控制每次介面請求的并發數,將30次分解成:并發數為5,分6次請求,這樣的話,服務器每次處理5次請求,資源釋放出來繼續處理下一批請求,從而解決并發擁堵問題~
初步構思:
class TaskQueue { constructor(max) { this.max = max; this.taskList = []; } addTask(task) { this.taskList.push(task); } } function createTask(i) { return () => { return new Promise((resolve, reject) => { setTimeout(() => { if (i == 4 || i == 15) { reject("出錯啦~"); } else { resolve("成功呀~" + i); } }, 2000); }); }; } const taskQueue = new TaskQueue(5); for (let i = 0; i < 30; i++) { const task = createTask(i); taskQueue.addTask(task); }
for回圈呼叫函式createTask()回傳30個promise的異步任務,任務佇列TaskQueue類回傳一個實體,控制這30個異步任務的并發,構造器中傳入并發數5,
接下來用TaskQueue實作控制并發:
class TaskQueue { constructor(max) { this.max = max; // 并發數 this.min = 0; this.taskList = []; // 全部任務 Promise.resolve().then(() => this.run()) // 等同步代碼(addTask)全部執行完成,再執行run } // 增加任務 addTask(task) { this.taskList.push(task); } // 執行任務 async run() { if (!this.taskList.length) return; const AsyncTasks = []; this.min = Math.min(this.max, this.taskList.length) // 當傳入的并發數大于任務數,取任務數, 反之取并發數 // 根據并發數分組 for(let i = 0; i < this.min; i++) { AsyncTasks.push(this.taskList.shift()); } await this.handleTask(AsyncTasks); // 通過下面遞回,這里將會有6個異步任務串聯執行 this.run(); // 遞回 } async handleTask(tasks) { // 回傳promise處理異步任務組 return new Promise(resolve => { // 遍歷任務組,5個異步任務并聯執行 tasks.forEach(async (task, index) => { await task().then(res => { console.log(res); }).catch((err) => { console.log(err); }).finally(() => { index + 1 === this.min && console.log('==============================='); index + 1 === this.min && resolve() // 最后一個任務resolve(),promise完成 }) }) }) } } function createTask(i) { return () => { return new Promise((resolve, reject) => { setTimeout(() => { if (i == 4 || i == 15) { // 測驗捕捉錯誤 reject("出錯啦~"); } else { resolve("成功呀~" + i); } }, 2000); }); }; } const taskQueue = new TaskQueue(5); for (let i = 0; i < 30; i++) { const task = createTask(i); taskQueue.addTask(task); }
試試效果:
nice,至此,30次異步任務,分6次完成,每次處理5個,大家可以在此基礎上拓展請求介面,并增加一些處理邏輯,歡迎留言探討~
腳踏實地行,海闊天空飛~
搜索
復制
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/500817.html
標籤:JavaScript
上一篇:報表開發的那些日子:關于EasyUI DataGrid動態列資料系結
下一篇:day 06 字串
