JavaScript實(shí)現(xiàn)帶并發(fā)限制的異步調(diào)度器
題目
實(shí)現(xiàn)一個(gè)帶并發(fā)限制的異步調(diào)度器 Scheduler
,保證同時(shí)運(yùn)行的任務(wù)最多有N個(gè)。完善下面代碼中的Scheduler
類,使得以下程序能正確輸出:
class Scheduler { add(promiseCreator) { ... } // ... } const timeout = (time) => new Promise(resolve => { setTimeout(resolve, time) }) const scheduler = new Scheduler(n) const addTask = (time, order) => { scheduler.add(() => timeout(time)).then(() => console.log(order)) } addTask(1000, '1') // 任務(wù)1 addTask(500, '2') // 任務(wù)2 addTask(300, '3') // 任務(wù)3 addTask(400, '4') // 任務(wù)4 // 打印順序是:2 3 1 4
題目分析
假設(shè)N為2,也就是保證同時(shí)運(yùn)行的任務(wù)有2個(gè)。那么在執(zhí)行addTask
4步操作之后,整體的流程應(yīng)該是這樣的。
- 起始1、2兩個(gè)任務(wù)開始執(zhí)行;
- 500ms時(shí),2任務(wù)執(zhí)行完畢,輸出2,任務(wù)3開始執(zhí)行;
- 800ms時(shí),3任務(wù)執(zhí)行完畢,輸出3,任務(wù)4開始執(zhí)行;
- 1000ms時(shí),1任務(wù)執(zhí)行完畢,輸出1,此時(shí)只剩下4任務(wù)在執(zhí)行;
- 1200ms時(shí),4任務(wù)執(zhí)行完畢,輸出4;
為什么會(huì)出現(xiàn)這樣的結(jié)果?我們來具體分析一下
首先連續(xù)執(zhí)行了4次addTask
,由于只能同時(shí)運(yùn)行的任務(wù)有2個(gè),所以,任務(wù)1和任務(wù)2將直接運(yùn)行,任務(wù)1將在1000ms之后運(yùn)行,任務(wù)2將在500ms之后運(yùn)行,所以,任務(wù)2肯定會(huì)比任務(wù)1執(zhí)行的快。當(dāng)任務(wù)2執(zhí)行完畢之后,輸出2。緊接著執(zhí)行任務(wù)3,此時(shí)任務(wù)1執(zhí)行也就經(jīng)過了500ms,還有500ms沒有執(zhí)行完,而任務(wù)3只需要300ms就執(zhí)行完畢,所以任務(wù)3也會(huì)比任務(wù)1執(zhí)行的快。又過了300ms(共計(jì)過了800ms)任務(wù)3執(zhí)行完畢,輸出3。任務(wù)4開始執(zhí)行,任務(wù)4需要400ms執(zhí)行完畢,而任務(wù)1目前只需要200ms,所以任務(wù)1會(huì)比任務(wù)4先執(zhí)行,200ms之后(共計(jì)1000ms)任務(wù)1執(zhí)行完畢,輸出1,在過了200ms(共計(jì)1200ms),任務(wù)4執(zhí)行完畢,輸出4。
下面我們用圖來表示一下
知道了這道題目具體要干啥了,下面就來看看代碼是如何實(shí)現(xiàn)的
代碼實(shí)現(xiàn)
直接上完整代碼好了~
class Scheduler { constructor(max) { this.max = max; this.count = 0; // 用來記錄當(dāng)前正在執(zhí)行的異步函數(shù) this.queue = new Array(); // 表示等待隊(duì)列 } async add(promiseCreator) { /* 此時(shí)count已經(jīng)滿了,不能執(zhí)行本次add需要阻塞在這里,將resolve放入隊(duì)列中等待喚醒, 等到count<max時(shí),從隊(duì)列中取出執(zhí)行resolve,執(zhí)行,await執(zhí)行完畢,本次add繼續(xù) */ if (this.count >= this.max) { await new Promise((resolve, reject) => this.queue.push(resolve)); } this.count++; let res = await promiseCreator(); this.count--; if (this.queue.length) { // 依次喚醒a(bǔ)dd // 若隊(duì)列中有值,將其resolve彈出,并執(zhí)行 // 以便阻塞的任務(wù),可以正常執(zhí)行 this.queue.shift()(); } return res; } } const timeout = time => new Promise(resolve => { setTimeout(resolve, time); }); const scheduler = new Scheduler(2); const addTask = (time, order) => { //add返回一個(gè)promise,參數(shù)也是一個(gè)promise scheduler.add(() => timeout(time)).then(() => console.log(order)); }; addTask(1000, '1'); addTask(500, '2'); addTask(300, '3'); addTask(400, '4'); // output: 2 3 1 4
這塊代碼中我們主要加了add部分。
首先我們來分析一下 Scheduler
這個(gè)類。max
表示同時(shí)可以執(zhí)行任務(wù)的最大數(shù)量。count
用來記錄當(dāng)前正在執(zhí)行的異步函數(shù)。每次addTask
都會(huì)通過scheduler.add
添加一個(gè)異步任務(wù)。
進(jìn)入add
函數(shù)中,首先需要做的事情是當(dāng)前已經(jīng)正在執(zhí)行的任務(wù)有沒有到達(dá)最大的任務(wù)數(shù)。
如果沒有達(dá)到最大的任務(wù)數(shù)(比如剛開始的加入任務(wù)一和任務(wù)二,此時(shí)任務(wù)是空的),每次執(zhí)行await promiseCreator();
這一步的時(shí)候,使用async
await
,當(dāng)promiseCreator
沒有執(zhí)行完畢的時(shí)候,會(huì)阻塞后面的任務(wù)。所以當(dāng)前兩個(gè)任務(wù)被addTask
加入的時(shí)候,執(zhí)行add
的時(shí)候,都會(huì)阻塞后面的任務(wù)。而我們的四個(gè)任務(wù)連續(xù)被加入的。當(dāng)add
任務(wù)三和任務(wù)四的時(shí)候,發(fā)現(xiàn)此時(shí)count
已經(jīng)滿了,所以需要阻塞在這里,將resolve
放入隊(duì)列中等待喚醒嗎,具體什么時(shí)候被喚醒呢?,當(dāng)前面的任務(wù)有任何一個(gè)執(zhí)行完畢之后,就可以被喚醒。這里使用queue
來維護(hù)resolve
,add
任務(wù)三和任務(wù)四的時(shí)候,會(huì)先后給queue
推入這兩個(gè)promise
的resolve
。
經(jīng)過500ms,任務(wù)二會(huì)先執(zhí)行完畢,也就是await promiseCreator();
執(zhí)行完畢之后,打印2,然后繼續(xù)之后后續(xù)的代碼,此時(shí)從queue
里面將第一個(gè)resolve
彈出,并執(zhí)行。執(zhí)行之后,任務(wù)3也就不再阻塞了,將繼續(xù)執(zhí)行await promiseCreator();
.
再經(jīng)過300ms任務(wù)三先執(zhí)行完畢之后(任務(wù)還在繼續(xù)執(zhí)行中),打印3,然后繼續(xù)之后后續(xù)的代碼,此時(shí)從queue
里面將resolve彈出,并執(zhí)行。執(zhí)行之后,任務(wù)4也就不再阻塞了,將繼續(xù)執(zhí)行await promiseCreator();
.
再經(jīng)過200ms任務(wù)一終于執(zhí)行完畢之后,打印1,然后繼續(xù)之后后續(xù)的代碼,此時(shí)queue
里面已經(jīng)是空的了
再經(jīng)過200ms任務(wù)四執(zhí)行完畢,打印4
到此這篇關(guān)于JavaScript實(shí)現(xiàn)帶并發(fā)限制的異步調(diào)度器的文章就介紹到這了,更多相關(guān)JavaScript異步調(diào)度器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript 判斷 object 的特定類轉(zhuǎn)載
Javascript 判斷 object 的特定類轉(zhuǎn)載...2007-02-02Google Map API更新實(shí)現(xiàn)用戶自定義標(biāo)注坐標(biāo)
由于工作需要,又要開始看Google Map API 代碼,今天再把我之前的GoogleMap類,又更新了下,加了個(gè)簡(jiǎn)單的用戶自定義標(biāo)注坐標(biāo)的功能??纯窗?代碼沒怎么優(yōu)化,別見笑)2009-07-07Bootstrap popover 實(shí)現(xiàn)鼠標(biāo)移入移除顯示隱藏功能方法
下面小編就為大家分享一篇Bootstrap popover 實(shí)現(xiàn)鼠標(biāo)移入移除顯示隱藏功能方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01php的派發(fā)機(jī)制實(shí)現(xiàn)方法
PHP是一種動(dòng)態(tài)類型的編程語言,它支持面向?qū)ο缶幊?在PHP中,派發(fā)指在運(yùn)行時(shí)確定要調(diào)用的方法或函數(shù)的過程,派發(fā)機(jī)制允許根據(jù)實(shí)際對(duì)象的類型來選擇要執(zhí)行的方法,這種靈活性使得PHP可以實(shí)現(xiàn)多態(tài)性,本文將給大家介紹php的派發(fā)機(jī)制是怎么實(shí)現(xiàn)的,需要的朋友可以參考下2023-10-10DeviceOne 讓你一見鐘情的App快速開發(fā)平臺(tái)
DeviceOne是一個(gè)非常先進(jìn)的App開發(fā)平臺(tái),使用Javascript 構(gòu)建原生體驗(yàn)的移動(dòng)應(yīng)用程序,DeviceOne主要關(guān)注外觀和體驗(yàn),以及和你的應(yīng)用程序的 UI 交互2016-02-02Bootstrap中表單控件狀態(tài)(驗(yàn)證狀態(tài))
這篇文章主要介紹了Bootstrap中表單控件狀態(tài)(驗(yàn)證狀態(tài)) 的相關(guān)資料,還給大家介紹了在Bootstrap框架中提供的機(jī)制驗(yàn)證效果,非常不錯(cuò),需要的朋友可以參考下2016-08-08JavaScript實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建CSS樣式規(guī)則方案
這篇文章主要介紹了JavaScript實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建CSS樣式規(guī)則方案,本文包含獲取樣式表、創(chuàng)建樣式表、插入規(guī)則、添加規(guī)則等內(nèi)容,需要的朋友可以參考下2014-09-09