前言
效能最佳化這一塊在前端是一個比較重要並且常考的知識,小編最近在面試的過程中也是被面試官問到了如何處理,比如前端現在有100個請求需要傳送給後端,一次性全部發送必然會造成伺服器的壓力很大,
所以此時我們需要控制併發數量,每次只能傳送5個請求,面試官問你這種場景你會如何實現?
正文
設計思路
比如把這十個任務全部裝到任務佇列裡,然後一次性把佇列裡的前兩個取出來執行即可,取到兩個後就不在取了,等到某個任務執行結束了之後我們再去遞迴一下取任務的這個行為,取出任務來去執行
這裏我們可以考慮使用佇列去請求大量介面。
思路如下:
假定最大併發數是paralleCount=5,我們對介面進行了定義編號,當請求佇列池中有一個請求返回後,就向池子中新增一個介面進行請求,依次直到最後一個請求執行完畢。
爲了模擬傳送起步請求這一操作,這裏咱們透過定時器定義一個timeout函式
來實現,傳入一個時間表示請求耗時。
function timeout(time){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(); },time) }) }
接下來咱們定義一個控制併發的類來專門來實現控制併發這麼一個功能,建構函式可以接受一個引數來表示每次的併發數量。裡面有一個任務陣列tasks用來儲存非同步任務,還有一個變數runningCount表示正在執行的任務。
class SuperTask{ constructor(paralleCount = 2){ this.paralleCount = paralleCount;//併發量 this.tasks = [] this.runningCount = 0;//正在執行的任務量 } }
接著咱們還需要再裡面定義一個add方法來新增非同步任務,這個add會返回一個Promise.
const superTask = new SuperTask(); superTask.add(()=>timeout(time)) .then(()=>console.log(`任務完成`))
add(task){//新增任務 return new Promise( (resolve,reject)=>{ this.tasks.push({//在陣列裡面新增非同步任務 task,resolve,reject }) this._run();//這裏的this指向的是例項物件 }) }
然後當add方法接收到非同步任務之後會將任務放進任務佇列中,關鍵在於如何去觸發這些任務並且控制併發數量為5呢?這個_run方法就是用來依次執行任務的。
_run(){ //依次執行任務 while(this.runningCount<this.paralleCount&&this.tasks.length){ const {task,resolve,reject} = this.tasks.shift(); this.runningCount++; task().then(resolve,reject).finally(()=>{ //不管任務成功還是失敗,任務執行完畢 this.runningCount--; this._run(); }) } }
這裏咱們透過判斷任務陣列中有任務並且當前正在執行的任務數小於併發數時,將任務佇列的任務取出並且執行他,不管任務成功還是失敗,任務執行完畢都要將當前併發數減一,並且遞迴呼叫run方法。這樣我們就實現了控制併發的這麼一個效果。
最後咱們來測試一下效果
const superTask = new SuperTask(); function addTask(time,name){ superTask.add(()=>timeout(time)) .then(()=>console.log(`任務${name}完成`)) } addTask(10000,1) addTask(2000,2) addTask(1000,3) addTask(1000,4) addTask(3000,5) addTask(2000,6) addTask(4000,7) addTask(2000,8)
完美!
最後附上全部的程式碼:
function timeout(time) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time); }); } // timeout(1000) // timeout(1000) // timeout(1000) // ... class SuperTask { constructor(paralleCount = 2) { this.paralleCount = paralleCount; // 併發量 this.tasks = []; this.runningCount = 0; // 正在執行的任務量 } add(task) { return new Promise((resolve, reject) => { this.tasks.push({ // 10 task, resolve, reject, }); this._run(); }); } _run() { // 依次執行tasks中的任務 while (this.runningCount < this.paralleCount && this.tasks.length) { const { task, resolve, reject } = this.tasks.shift(); this.runningCount++; task() .then(resolve, reject) .finally(() => { this.runningCount--; this._run(); }); } } } const superTask = new SuperTask(); function addTask(time, name) { superTask .add(() => timeout(time)) .then(() => { console.log(`任務${name}完成`); }); } addTask(10000, 1); addTask(2000, 2); addTask(5000, 3); addTask(1000, 4); addTask(7000, 5); addTask(3000, 6);