摘要:在這種情況下,馬上可以想到的一個方法是我們可以使用隊列的數據結構來實現調度粒度為的方法。
實現一個并發數可變的 Promise.all 靜態方法
Promise.all (iterable): The all function returns a new promise which is fulfilled with an array of fulfillment values for the passed promises, or rejects with the reason of the first passed promise that rejects. It resolves all elements of the passed iterable to promises as it runs this algorithm.
Promise.all 靜態方法具有如下特性:
接收一個 Promise 實例的數組或具有 Iterator 接口的對象
如果元素不是 Promise 對象則使用 Promise.resolve 轉成 Promise 對象
如果全部成功狀態變為 resolved,返回值將有序組成一個數組傳給回調
只要有一個失敗狀態就變為 rejected,返回值將直接傳遞給回調
該方法的返回值是一個新的 Promise 對象
下面是 Promise.all 的簡單用法:
const p1 = Promise.resolve(1); const p2 = Promise.resolve(2); const p3 = Promise.resolve(3); Promise.all([p1, p2, p3]) .then((results) => { console.log(results); // [1, 2, 3] });
const p1 = Promise.resolve(1); const p2 = Promise.reject(2); const p3 = Promise.resolve(3); Promise.all([p1, p2, p3]) .then((results) => { console.log(results); }).catch((e) => { console.log(e); // 2 });如何實現 Promise.all 靜態方法
Promise.all = Promise.all || function(promises) { // 如果實參不是數組則報錯返回 if (!isArray(promises)) { throw new TypeError("You must pass an array to all."); } // 結果返回一個新的 Promise 實例 return new Promise(function(resolve, reject) { var i = 0, result = [], len = promises.length, count = len // 使用閉包記錄數組執行順序 function resolver(index) { return function(value) { resolveAll(index, value); }; } // 只要有一個失敗狀態就變為 rejected function rejecter(reason) { reject(reason); } // 如果全部成功狀態變為 resolved function resolveAll(index, value) { result[index] = value; if (--count == 0) { resolve(result) } } // 遍歷數組并發執行異步代碼 for (; i < len; i++) { promises[i].then(resolver(i), rejecter); } }); }實現一個調度粒度可變的 Promise.all 靜態方法
那么回到題目的問題:如何實現一個調度粒度可變的 Promise.all 靜態方法呢?這里首先可能會產生一個疑問就是什么叫調度粒度可變,實際上很簡單:就是給 all 方法增加一個正整數類型的參數,用來標識傳入的 Promise 實例數組中可以并發執行的最大個數。
舉例如下,聲明三個不同異步時間的 Promise 實例并調用 all 方法,正常情況下(我們粗暴的認為)其執行時間應該為200ms(當然這是一個錯誤的答案,在這個例子當中我們僅考慮并發邏輯),而在調度粒度為1的 all 方法中其執行時間應該為450ms,在調度粒度為2的 all 方法中其執行時間應該為250ms,以此類推。
const p1 = new Promise((resolve) => setTimeout(() => resolve(1), 150)); const p2 = new Promise((resolve) => setTimeout(() => resolve(2), 200)); const p3 = new Promise((resolve) => setTimeout(() => resolve(3), 100)); Promise.all([p1, p2, p3]).then((r) => console.log(r));
在這種情況下,馬上可以想到的一個方法是:我們可以使用隊列的數據結構來實現調度粒度為1的 all 方法。
Promise.all1 = (promises) => { if (!isArray(promises)) { throw new TypeError("You must pass an array to all."); } return new Promise((resolve, reject) => { const _q = [...promises]; const result = []; function resolver(value) { result.push(value); next(); } function rejecter(reason) { reject(reason); } function next() { if (_q.length) { _q.shift().then(resolver, rejecter); } else { resolve(result); } } next(); }); }
寫到這兒不難發現,不同調度粒度實際上是對隊列每次推出的 Promise 實例數量最大值的約束,以及對返回結果的順序索引作出緩存,那么把代碼進行簡單的修改即可實現預期的功能。
Promise.all2 = (promises, concurrent = promises.length) => { if (!Array.isArray(promises)) { throw new TypeError("You must pass an array to all."); } if (concurrent < 1) { return Promise.reject(); } return new Promise((resolve, reject) => { const queue = [...promises]; const result = []; let total = promises.length; let count = concurrent; let index = 0; function resolver(index) { return function(value) { resolveAll(index, value); }; } function resolveAll(index, value) { result[index] = value; count++; total--; next(); if (!total) { resolve(result); } } function rejecter(reason) { reject(reason); } function next() { while (queue.length && count > 0) { count--; (queue.shift())().then(resolver(index++), rejecter); } } next(); }); };
當然這里有一個比較吊詭的地方!如果我們按照上面的代碼進行 Promise 實例的聲明,那么在執行到 all 方法之前它們就已經在并發執行,也就不會有“不同調度粒度”之說,所以為了實現我們預期的功能需要在 all 方法內部把這些 Promise 實例初始化出來。
function promiseFactory(fn) { return function() { return new Promise(fn); }; } const p1 = promiseFactory((resolve) => setTimeout(() => resolve(1), 1500)); const p2 = promiseFactory((resolve) => setTimeout(() => resolve(2), 2000)); const p3 = promiseFactory((resolve) => setTimeout(() => resolve(3), 1000)); console.time("hello world!"); Promise.all2([p1, p2, p3], 1).then((r) => { console.log(r) console.timeEnd("hello world!"); // 4500+ms打印結果 }); Promise.all2([p1, p2, p3], 2).then((r) => { console.log(r) console.timeEnd("hello world!"); // 2500+ms打印結果 }); Promise.all2([p1, p2, p3]).then((r) => { console.log(r) console.timeEnd("hello world!"); // 2000+ms打印結果 });
為了能看出明顯的區別我們把定時器的時間延長到秒,執行代碼后完美驗證。
至此結束。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94333.html
摘要:并發限制指的是,每個時刻并發執行的數量是固定的,最終的執行結果還是保持與原來的一致。換句話說,就是把生成數組的控制權,交給并發控制邏輯。 背景 通常,我們在需要保證代碼在多個異步處理之后執行,會用到: Promise.all(promises: []).then(fun: function); Promise.all可以保證,promises數組中所有promise對象都達到resol...
摘要:最近在寫一個程序,功能是下載頁面上的資源,首先拿到頁面資源鏈接列表,如要求是資源并行下載,所有資源下載結束后通知,收集錯誤的下載鏈接。如果把上面模擬請求的注釋去掉,還會發現是在結束后就執行了,而后面的請求還未結束。 最近在寫一個Node.js程序,功能是下載頁面上的資源,首先拿到頁面資源鏈接列表,如: [ https://xxx.com/img/logo.jpg, https:...
摘要:第二種則一定會執行所有的異步函數,即便你需要使用的是這些高階函數。并發實現的異步數組然后修改,使用即可上面的其他內容終結整個鏈式操作并返回結果這里使用是為了兼容的調用方式調用方式不變。 JavaScript 異步數組 吾輩的博客原文: https://blog.rxliuli.com/p/5e... 場景 吾輩是一只在飛向太陽的螢火蟲 JavaScript 中的數組是一個相當泛用性的...
摘要:本次的任務假如。。。。。引擎發生了重大故障,方法變成了,為了拯救世界,需要開發一個模塊來解決此問題。實現首先要知道是什么是對異步編程的一種抽象。數組中任何一個為的話,則整個調用會立即終止,并返回一個的新的對象。 本次的任務 假如。。。。。 JavaScript v8 引擎發生了重大故障,Promise.all 方法變成了 undefined ,為了拯救 JavaScript 世界,需要...
摘要:并發模塊本身有兩種不同的類型進程和線程,兩個基本的執行單元。調用以啟動新線程。在大多數系統中,時間片發生不可預知的和非確定性的,這意味著線程可能隨時暫停或恢復。 大綱 什么是并發編程?進程,線程和時間片交織和競爭條件線程安全 策略1:監禁 策略2:不可變性 策略3:使用線程安全數據類型 策略4:鎖定和同步 如何做安全論證總結 什么是并發編程? 并發并發性:多個計算同時發生。 在現代...
閱讀 2299·2021-11-24 09:38
閱讀 2122·2021-11-22 14:44
閱讀 1157·2021-07-29 13:48
閱讀 2622·2019-08-29 13:20
閱讀 1120·2019-08-29 11:08
閱讀 2061·2019-08-26 10:58
閱讀 1267·2019-08-26 10:55
閱讀 3163·2019-08-26 10:39