后端接口太慢,前端如何優雅地實現一個“請求隊列”,避免并發打爆服務器?
有這樣一些場景:
- 頁面一加載,需要同時發 10 個請求,結果頁面卡住,服務器也快崩了。
- 用戶可以批量操作,一次點擊觸發了幾十個上傳文件的請求,瀏覽器直接轉圈圈。
當后端處理不過來時,前端一股腦地把請求全發過去,只會讓情況更糟。
核心思想就一句話:不要一次性把所有請求都發出去,讓它們排隊,一個一個來,或者一小批一小批來。
這就好比超市結賬,只有一個收銀臺,卻來了100個顧客。最好的辦法就是讓他們排隊,而不是一擁而上。我們的“請求隊列”就是這個“排隊管理員”。

直接上代碼:一個即插即用的請求隊列
不用復雜的分析,直接復制下面的 RequestPool 類到我們的項目里。它非常小巧,只有不到 40 行代碼。
/**
* 一個簡單的請求池/請求隊列,用于控制并發
* @example
* const pool = new RequestPool(3); // 限制并發數為 3
* pool.add(() => myFetch('/api/1'));
* pool.add(() => myFetch('/api/2'));
*/
class RequestPool {
/**
* @param {number} limit - 并發限制數
*/
constructor(limit = 3) {
this.limit = limit; // 并發限制數
this.queue = []; // 等待的請求隊列
this.running = 0; // 當前正在運行的請求數
}
/**
* 添加一個請求到池中
* @param {Function} requestFn - 一個返回 Promise 的函數
* @returns {Promise}
*/
add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this._run(); // 每次添加后,都嘗試運行
});
}
_run() {
// 只有當 正在運行的請求數 < 限制數 且 隊列中有等待的請求時,才執行
while (this.running < this.limit && this.queue.length > 0) {
const { requestFn, resolve, reject } = this.queue.shift(); // 取出隊首的任務
this.running++;
requestFn()
.then(resolve)
.catch(reject)
.finally(() => {
this.running--; // 請求完成,空出一個位置
this._run(); // 嘗試運行下一個
});
}
}
}如何使用?三步搞定!
假設你有一個請求函數 mockApi,它會模擬一個比較慢的接口。

發生了什么?
當你運行上面的代碼,你會看到:
- [1] 和 [2] 的請求幾乎同時開始。
- [3]、[4]、[5]、[6] 在乖乖排隊。
- 當 [1] 或 [2] 中任意一個完成后,隊列中的 [3] 馬上就會開始。
- 整個過程,同時運行的請求數永遠不會超過 2 個。
控制臺輸出類似這樣:
[1] ?? 請求開始...
[2] ?? 請求開始...
// (此時 3, 4, 5, 6 在排隊)
[1] ? 請求完成!
[1] 收到結果: 任務 1 的結果
[3] ?? 請求開始... // 1號完成,3號立刻補上
[2] ? 請求完成!
[2] 收到結果: 任務 2 的結果
[4] ?? 請求開始... // 2號完成,4號立刻補上
...它是如何工作的?
(1) add(requestFn): 你扔給它的不是一個已經開始的請求,而是一個“啟動器”函數 () => mockApi(i)。它把這個“啟動器”放進 queue 數組里排隊。
(2) _run(): 這是管理員。它會檢查:
- 現在有空位嗎?(running < limit)
- 有人在排隊嗎?(queue.length > 0)
- 如果兩個條件都滿足,就從隊首叫一個號(queue.shift()),讓它開始工作(執行 requestFn()),并且把正在工作的計數 running 加一。
(3) .finally(): 這是最關鍵的一步。每個請求不管是成功還是失敗,最后都會執行 finally 里的代碼。它會告訴管理員:“我完事了!”,然后把 running 減一,并再次呼叫管理員 _run() 來看看能不能讓下一個人進來。
這樣就形成了一個完美的自動化流程:完成一個,就自動啟動下一個。
以后再遇到需要批量發請求的場景,別再用 Promise.all 一股腦全發出去了。
把上面那段小小的 RequestPool 代碼復制到你的項目里,用它來包裹我們的請求函數。只需要設置一個合理的并發數(比如 2 或 3),就能在不修改后端代碼的情況下,大大減輕服務器的壓力,讓我們的應用運行得更平穩。
這是一種簡單、優雅且非常有效的前端優化手段。






























