Last active
May 25, 2025 02:02
-
-
Save psenger/491c107e4f754b57d729c931bde7493a to your computer and use it in GitHub Desktop.
[Design Pattern: Promise All - Deconstructed with Concurrency] #JavaScript #Promise
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const assert = require('assert'); | |
| /** | |
| * Promise ALL, technique v1 - an approach to understanding Promise.All | |
| * @param promises | |
| * @returns {*} | |
| * @constructor | |
| */ | |
| const PromiseAll = function PromiseAll ( promises = [] ) { | |
| promises = promises === null ? [] : promises; | |
| const results = []; | |
| let completedPromises = 0; | |
| return new Promise( (resolve, reject) => { | |
| promises.forEach( (promise, index) => { | |
| promise.then( value => { | |
| results[index] = value; | |
| completedPromises += 1; | |
| if(completedPromises === promises.length) { | |
| resolve(results); | |
| } | |
| }).catch(function (error) { | |
| reject(error); | |
| }); | |
| }); | |
| }); | |
| }; | |
| /** | |
| * Promise ALL, technique v2 - an approach to understanding Promise.All | |
| * @param promises | |
| * @returns {PromiseLike<[]> | Promise<[]>} | |
| * @constructor | |
| */ | |
| const PromiseSeqSettle = function PromiseSeqSettle(promises = []) { | |
| promises = promises === null ? [] : promises; | |
| const results = []; | |
| const reducer = (accum, p) => { | |
| return accum.then(() => p).then(r => results.push(r)).then(() => Promise.resolve()); | |
| }; | |
| return promises.reduce(reducer, Promise.resolve()) | |
| .then(() => results); | |
| }; | |
| /** | |
| * Promise ALL, with concurrency control | |
| * @param promises | |
| * @param n - number of concurrent blocks | |
| * @returns {PromiseLike<[]> | Promise<[]>} | |
| * @constructor | |
| */ | |
| const PromiseAllConcurrent = function PromiseAllConcurrent(promises = [], n) { | |
| const chunk = function chunk(array, size) { | |
| if(array.length <= size){ | |
| return [array] | |
| } | |
| return [array.slice(0,size), ...chunk(array.slice(size), size)] | |
| }; | |
| promises = promises === null ? [] : promises; | |
| const chunked = chunk(promises,n); | |
| const results = []; | |
| const reducer = (accum, p) => { | |
| return accum.then(() => Promise.all(p)).then(r => results.push(...r)).then(() => Promise.resolve()); | |
| }; | |
| return chunked.map(chunkedMap=>chunkedMap).reduce(reducer, Promise.resolve()) | |
| .then(() => results); | |
| }; | |
| const later = (delay) => { | |
| return new Promise(function(resolve) { | |
| setTimeout(resolve, delay); | |
| }); | |
| }; | |
| const getRandomInt = (min, max) => { | |
| return Math.floor(Math.random() * (max - min + 1)) + min; | |
| }; | |
| const ranDm = (index,rnd) => later(rnd).then(() => { | |
| return {index, msg: rnd}; | |
| }); | |
| const buildTest = (length) => new Array( length ).fill( null ).map((value,index)=>ranDm(index,getRandomInt(500,1000))); | |
| /** | |
| * Both of these techniques, perform well. I would like to see the call stack of a big test for | |
| * a better understanding how they hold up to large amounts of data. | |
| */ | |
| console.time('PromiseAll A'); | |
| PromiseAll(buildTest(100)).then((a)=>{ | |
| console.timeEnd('PromiseAll A'); | |
| }).catch ((error)=>{ | |
| console.log(error); | |
| }); | |
| console.time('PromiseSeqSettle B'); | |
| PromiseSeqSettle(buildTest(100)).then((a)=>{ | |
| console.timeEnd('PromiseSeqSettle B'); | |
| }).catch ((error)=>{ | |
| console.log(error); | |
| }); | |
| console.time('PromiseAllConcurrent A'); | |
| const testLength = 10000; | |
| PromiseAllConcurrent(buildTest(testLength),20).then((a)=>{ | |
| console.timeEnd('PromiseAllConcurrent A'); | |
| assert(a.length === testLength ) | |
| a.forEach((v,i)=>{ | |
| assert(v.index === i ) | |
| }) | |
| }).catch ((error)=>{ | |
| console.log(error); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment