JavaScript 任务池
JavaScript 任務(wù)池
線程池
在多線程語(yǔ)言中 ,我們通常不會(huì)隨意的在需要啟動(dòng)線程的時(shí)候去啟動(dòng) ,而是會(huì)選擇創(chuàng)建一個(gè)線程池 。所謂線程池,本意其實(shí)就是(不止這些作用,其余作用可以自行查閱):
- 節(jié)省操作系統(tǒng)資源
- 限制最大線程數(shù) 。
對(duì)于 JavaScript 來(lái)說 ,雖然不存在“啟動(dòng)線程”這種問題,但我們還是可以通過類似的思想,來(lái)限制我們做異步操作的數(shù)量 。
分析
首先我們需要一個(gè)數(shù)組,用它來(lái)存儲(chǔ)尚未執(zhí)行的任務(wù) ,每個(gè)任務(wù)都是一個(gè)函數(shù),這個(gè)函數(shù)必須要返回一個(gè) Promise。
type Task = () =>Promise;const tasks: Task[] = []; 其次我們需要一個(gè)方法來(lái)進(jìn)行任務(wù)的添加。
function addTask(task: Task): void;最后我們需要一個(gè)函數(shù)來(lái)執(zhí)行我們所有的 task 。
而在這之前 ,我們還需要定義一個(gè)值,來(lái)定義同時(shí)執(zhí)行的異步任務(wù)的最大數(shù)量。
function execTasks(): void;實(shí)現(xiàn)
根據(jù)我們的分析 ,我們可以寫下基礎(chǔ)的代碼如下:
interface TaskPool { addTask(task: Task): void;}type Task = () =>Promise;function newTaskPool(max = 10): TaskPool { const tasks: Task[] = []; function addTask(task: Task): void { } function execTasks(): void { }} 新增任務(wù)非常簡(jiǎn)單,我們寫出如下代碼填充 addTask 。
function addTask(task: Task): void { tasks.push(task);}接下來(lái)就是重頭戲 。如何實(shí)現(xiàn) execTasks 方法來(lái)限制最大異步任務(wù)數(shù)量呢?
首先我們來(lái)明確一點(diǎn),在下面這個(gè)場(chǎng)景中,如果 foo 函數(shù)是異步操作,那么是不會(huì)阻塞我們的代碼執(zhí)行的。
console.log("Before");foo();console.log("After");那么我們可以這么操作:
- 定義一個(gè)變量用來(lái)記錄當(dāng)前的空閑任務(wù)數(shù)量;
- 執(zhí)行 execTasks 時(shí),會(huì)選取當(dāng)前任務(wù)數(shù)量和空閑任務(wù)數(shù)二者相比較小的一個(gè);
- 根據(jù)該值進(jìn)行循環(huán) ,每次循環(huán)彈出 tasks 第一位的任務(wù)進(jìn)行執(zhí)行;
- 執(zhí)行前將空閑任務(wù)數(shù) -1 ,執(zhí)行完畢后空閑任務(wù)數(shù) +1,并再次執(zhí)行 execTasks 。
let leisure = max;function execTasks(): void { if (tasks.length === 0) return; const execTaskNum = Math.min(tasks.length, leisure); for (let i = 0; i < execTaskNum; i++) { const task = tasks.shift(); if (!task) continue; leisure--; task().finally(() =>{ leisure++; execTasks(); }); }}最后我們只剩下了一個(gè)問題了