Skip to content

Instantly share code, notes, and snippets.

@park-brian
Last active June 23, 2020 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save park-brian/793a7193307c1a6308a794891756e601 to your computer and use it in GitHub Desktop.
Save park-brian/793a7193307c1a6308a794891756e601 to your computer and use it in GitHub Desktop.
promise-utils
/**
* Sometimes, we need a plain es5 function to chain execution of promises (eg: when
* targeting older browsers using polyfills, while eschewing a build step). This function
* takes an array of promises or functions which return promises, and applies the supplied
* callback function against each promise sequentially, waiting for resolution before
* executing/resolving the next promise.
*
* Promises always resolve in their order of appearance. However, this function only
* defers execution of promises if functions which return promises are provided (eg:
* using .bind to create bound functions).
*
* If the supplied callback function also returns a promise, then execution/resolution
* of the next promise will be deferred until the callback resolves.
*
* Returns a promise which resolves once the chain resolves.
*
* @example
* function addOne(num) {
* return new Promise(function(resolve, reject) {
* setTimeout(function() {
* resolve(num + 1);
* }, num * 1000);
* })
* }
*
* // bind functions to defer execution of promises until called
* var promises = [
* addOne.bind(null, 1),
* addOne.bind(null, 2),
* addOne.bind(null, 3)
* ];
*
* // forEachPromise resolves once the last promise/callback has finished
* forEachPromise(promises, function(result, i) {
* console.log('result', result, 'index', i);
* return new Promise(function(resolve, reject) {
* console.log('adding 100ms delay before continuing');
* setTimeout(function() {
* resolve(null);
* }, 100);
* });
* }).then(function() {
* console.log('Finished execution');
* });
*
* @param promises {Promise<any>[] | (() => Promise<any>)[]} An array of promises,
* functions which return promises, or promises that return promises
* @param callback {(resolvedValue: any, index: number, promiseArray: (Promise<any>[] | (() => Promise<any>)[]), resolvedPromise: Promise) => (Promise | any | void)}
* A callback to be applied to each promise's resolved value
* @returns {Promise} A promise which resolves once the last promise in the chain resolves
*/
function forEachPromise(promises, callback) {
var promiseChain;
promises.forEach(function(promise, i) {
// returns a promise which resolves after the callback finishes
function promiseCallback() {
var resolvedPromise = Promise.resolve(
promise instanceof Function ? promise() : promise
);
return resolvedPromise.then(function (result) {
return callback(result, i, promises, resolvedPromise);
});
}
// initializes the inital promise, then chains promises
promiseChain = i === 0
? promiseCallback()
: promiseChain.then(promiseCallback);
});
return promiseChain;
}
/**
* Takes an array of promises, functions which return promises and reduces the resolved
* values sequentially against the supplied callback function.
*
* Promises always resolve in their order of appearance. However, this function only
* defers execution of promises if functions which return promises are provided (eg:
* using .bind to create bound functions).
*
* If the supplied callback function also returns a promise, then execution/resolution
* of the next promise will be deferred until the callback resolves.
*
* Returns a promise which resolves with the reduced value once the chain resolves.
*
* @param promises {Promise<any>[] | (() => Promise<any>)[]} An array of promises,
* functions which return promises, or promises that return promises
* @param callback {(accumulator: any, resolvedValue: any, index: number, promiseArray: (Promise<any>[] | (() => Promise<any>)[]), resolvedPromise: Promise<any>) => (Promise<any> | any)}
* A callback to be applied to each promise's resolved value
* @returns {Promise} A promise which resolves once the last promise in the chain resolves
*/
function reducePromise(promises, callback, initialValue) {
var accumulator = initialValue;
var promiseChain;
promises.forEach(function(promise, i) {
// returns a promise which resolves after the callback finishes
function promiseCallback() {
var resolvedPromise = Promise.resolve(
promise instanceof Function ? promise() : promise
);
return resolvedPromise.then(function (result) {
// if an initial value is not supplied, use the first promise's resolved value
if (i === 0 && accumulator === undefined)
accumulator = result;
return callback(accumulator, result, i, promises, resolvedPromise);
}).then(function(callbackResult) {
accumulator = callbackResult;
return accumulator;
});
}
// initializes the inital promise, then chains promises
promiseChain = i === 0
? promiseCallback()
: promiseChain.then(promiseCallback);
});
return promiseChain;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment