在我們平時開發中,經常會遇到頁面數據初始化時,頻繁調同一個介面的情況。比如echarts項目中,一個頁面可能會有幾十張圖表,如果一個介面返回所有圖表數據的話,會造成用戶過長的等待時間,再者過多圖表同時渲染,也會給頁面增加壓力,造成卡頓的現象。 我們通常會讓每個圖表單獨調一個介面,入參不同,這樣更有利 ...
在我們平時開發中,經常會遇到頁面數據初始化時,頻繁調同一個介面的情況。比如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個,大家可以在此基礎上拓展請求介面,並增加一些處理邏輯,歡迎留言探討~
腳踏實地行,海闊天空飛~
搜索
複製