結(jié)合 Promise.all 和 Promise.race 功能的函數(shù)

今天掐松,公司內(nèi)大神問我 Promise 有沒有類似 Promise.all 多個并發(fā)執(zhí)行的并且保證數(shù)據(jù)的順序尤蛮,但是又可以完成一個異步操作就執(zhí)行異步的方法,比如:

這里有 5 個 http 分別耗時 [1000, 2000, 3000, 5000, 7000] ms 蜘拉,假如我使用 Promise.all 從完成請求到開始執(zhí)行回調(diào)一共需要時間是 7000ms

const arr = [1000, 200, 500, 700, 600];
const mockHttp = v => new Promise((resolve, reject) => {
    setTimeout(() => resolve(v), v);
});

const tasks = [];
arr.forEach(v => tasks.push(mockHttp(v)));

const cb = v => console.log(v);

(async () => {
    console.time();
    const result = await Promise.all(tasks);
    console.timeEnd();
    //  Get data, then do callback
    result.forEach(v => cb(v));
})()

//  Result
//  default: 1000.254150390625ms
//  1000
//  200
//  500
//  700
//  600

這樣雖然能保證拿到的結(jié)果是按照順序來的遂唧,但是,在執(zhí)行回調(diào)的時候卻是在等全部異步結(jié)束后之拨,我期望的是這樣的

//  1000
//  200
//  500
//  700
//  600
//  default: 1000.254150390625ms
  • 保證異步結(jié)果是按順序返回的
  • 并發(fā)

這個我自己感覺是 Promise.allPromise.race 的結(jié)合茉继,無奈沒有發(fā)現(xiàn) Promise 規(guī)范中沒有這種接口,于是就嘗試自己實現(xiàn)蚀乔。

當(dāng)時正好在看 Rxjs 烁竭,所以感覺這個剛好就很適合這種場景,于是就開始寫

首先吉挣,需要同樣需要 mock 異步的過程派撕,這里直接使用上面的 setTimeout 來mock 異步的過程。

const mockHttp = v => new Promise((resolve, reject) => {
    setTimeout(() => resolve(v), v);
})

然后睬魂,我們開始寫異步并發(fā)的代碼终吼,這里使用 Rxjs 的觀察者模式

const tasks = [3500, 300, 5000, 3000, 700, 2000, 5];
const a = Rx.Observable.create((obs) => {
    tasks.forEach((v, idx) => {
    //  Do something async but return value must include { value, index }
        mockHttp(v).then(data => obs.next({data, idx}))
    });
});

為了能拿到異步結(jié)果并且執(zhí)行,需要添加觀察者氯哮。

//  假如直接訂閱就沒有意義了际跪,不能讓異步的結(jié)果按照順序返回

function scheduler (input) {
    const queen = [];
    let idxObj = {};
    return Rx.Observable.create((obs) => {
        input.subscribe((data) => {
            if (data.idx === 0 || data.idx === idxObj.idx) {
                obs.next(data);
                idxObj.idx = data.idx + 1;
                //  For wait queen value
                it(idxObj, queen, obs)
            } else {
                queen.push(data);
            }
        })
    })
}

function it (idxObj, data, obs) {
    const newArr = Array.from(data).sort((a, b) => a.idx - b.idx)
    if (newArr.length !== 0 && newArr[0].idx === idxObj.idx) {
        obs.next(newArr[0])
        data.splice(data.findIndex(v => v.idx === newArr[0].idx), 1);
        idxObj.idx = newArr[0].idx + 1;
        it(idxObj, data, obs);
    }
}

這段代碼是整個功能的核心,主要作用就是增加一個隊列用于緩存排在后面但異步所需時間較短的返回值,然后在每一次返回值時去遍歷這個隊列將順序的值給返回出來姆打。因為 Rxjs 的觀察者訂閱模式良姆,可以較為簡單的實現(xiàn)這個功能,也可以使用 nodejsevent 庫來達到同樣的目的幔戏。

最后玛追,加上訂閱函數(shù),也就是handle 闲延,就能達到預(yù)期的效果了痊剖。

const handle = v => console.log(v);
scheduler(a).subscribe(v => {
  console.log(v);
  handle(v.data);
})

這里為了能看出執(zhí)行順序,所以把順序也給附加在返回值中慨代。

最后附上全部代碼鏈接 JS Fiddle

假如需要更直觀的看到執(zhí)行順序使用下面代碼在 Rxjs 頁面執(zhí)行邢笙。

console.time();
const tasks = [3500, 300, 5000, 3000, 700, 2000, 5];
const mockHttp = v => new Promise((resolve, reject) => {
    setTimeout(() => resolve(v), v);
})

const a = Rx.Observable.create((obs) => {
    tasks.forEach((v, idx) => {
    //  Do something async but return value must include { value, index }
        mockHttp(v).then(data => obs.next({data, idx}))
    });
});

function scheduler (input) {
    const queen = [];
    let idxObj = {};
    return Rx.Observable.create((obs) => {
        input.subscribe((data) => {
            if (data.idx === 0 || data.idx === idxObj.idx) {
                obs.next(data);
                idxObj.idx = data.idx + 1;
                //  For wait queen value
                it(idxObj, queen, obs)
            } else {
                queen.push(data);
            }
        })
    })
}

function it (idxObj, data, obs) {
    const newArr = Array.from(data).sort((a, b) => a.idx - b.idx)
    if (newArr.length !== 0 && newArr[0].idx === idxObj.idx) {
        obs.next(newArr[0])
        data.splice(data.findIndex(v => v.idx === newArr[0].idx), 1);
        idxObj.idx = newArr[0].idx + 1;
        it(idxObj, data, obs);
    }
}

const handle = v => { console.timeEnd(); console.log(v); console.time() }
scheduler(a).subscribe(handle)

總結(jié): 寫了這么多,就為了兩個目標侍匙, 一個是并發(fā)異步氮惯, 另一個是只要按照順序執(zhí)行 callback 并且不是等全部并發(fā)都完成。如有疏漏想暗,請指正妇汗,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末说莫,一起剝皮案震驚了整個濱河市杨箭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌储狭,老刑警劉巖互婿,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辽狈,居然都是意外死亡慈参,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門刮萌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驮配,“玉大人崔涂,你說我怎么就攤上這事箩兽「鸵簦” “怎么了祟牲?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稽莉。 經(jīng)常有香客問我课舍,道長霹俺,這世上最難降的妖魔是什么澎语? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任途事,我火速辦了婚禮验懊,結(jié)果婚禮上擅羞,老公的妹妹穿的比我還像新娘尸变。我一直安慰自己,他們只是感情好减俏,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布召烂。 她就那樣靜靜地躺著,像睡著了一般娃承。 火紅的嫁衣襯著肌膚如雪奏夫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天历筝,我揣著相機與錄音酗昼,去河邊找鬼。 笑死梳猪,一個胖子當(dāng)著我的面吹牛麻削,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播春弥,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼呛哟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了匿沛?” 一聲冷哼從身側(cè)響起扫责,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逃呼,沒想到半個月后鳖孤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡抡笼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年苏揣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔫缸。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡腿准,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拾碌,到底是詐尸還是另有隱情吐葱,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布校翔,位于F島的核電站弟跑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏防症。R本人自食惡果不足惜孟辑,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一哎甲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饲嗽,春花似錦炭玫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尽狠,卻和暖如春衔憨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背袄膏。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工践图, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沉馆。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓码党,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悍及。 傳聞我的和親對象是個殘疾皇子闽瓢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容