UNPKG

promises-arrow

Version:

`promises-arrow` is a library of (1) higher order functions, such as `map()` and `filter()`where the function returns a Promisel (2) Functions that wait, returning a Promise; (3) Functions that manage retrying an operation N times until it succeeds; (4,5)

136 lines (122 loc) 5.05 kB
import {DeferredPromise} from "./DeferredPromise"; export class HigherOrderPromise { static while<T>(fnContinue: () => boolean, fn: () => Promise<unknown>): Promise<unknown> { const promise = new DeferredPromise<any>(); whileLocal(fnContinue, fn, promise); return promise.promise; } static for<T>(start: number, pastEnd: number, increment: number, fn: (t: number) => Promise<any>): Promise<any> { let value = start; return HigherOrderPromise.while( () => value < pastEnd, () => fn(value).then(() => value += increment) ); } // Calls the function with each of the items in turn, one at a time. // It does not alter the items array static forEach<T>(items: Array<T>, fn: (item: T, index: number) => Promise<any>): Promise<any> { return HigherOrderPromise.for(0, items.length, 1, i => fn(items[i], i)); } // Calls the function with each of the iterator items in turn, one at a time, recursively. static forEachIterator<T>(it: Iterator<T>, fn: (item: T, index: number) => Promise<any>): Promise<any> { let index = 0 let head: IteratorResult<T>; return HigherOrderPromise.while( () => { head = it.next(); return !head.done; }, () => { index += 1 return fn(head.value, index-1) } ); } // Calls the function with each of the items in turn, N at a time. // It does not alter the items array static forEachWithConstrainedParallelism<T, U>(items: Array<T>, asynchCount: number, fn: (item: T) => Promise<unknown>): Promise<unknown> { if (asynchCount < 2 || items.length < asynchCount) { return HigherOrderPromise.forEach(items, fn); } const copy: Array<T> = Array.from(items); const workers: Array<any> = []; for (let i = 0; i < asynchCount; i++) { workers.push(forEachWorker(copy, fn)); } return Promise .all(workers) .then(arrays => arrays.reduceRight((a, b) => a.concat(b), [])); } // Calls the function with each of the items in turn, one at a time, // adding the result to the returned array in the same order. // Returns a mapped array // It does not alter the items array (and assumes that it doesn't during execution static map<T, U>(items: Array<T>, fn: (item: T) => Promise<U>): Promise<Array<U>> { const results: Array<U> = []; return HigherOrderPromise.for(0, items.length, 1, i => fn(items[i]).then(result => results.push(result))) .then(() => results); } // Calls the function with each of the items in turn, one at a time, // concatenating the result to the returned array in the same order. // Returns a flat-mapped array // It does not alter the items array (and assumes that it doesn't during execution static flatMap<T, U>(items: Array<T>, fn: (item: T) => Promise<Array<U>>): Promise<Array<U>> { let results: Array<U> = []; return HigherOrderPromise.for(0, items.length, 1, i => fn(items[i]).then(result => results = results.concat(result))) .then(() => results); } // Calls the predicate function with each of the items in turn, one at a time to select. // Returns a filtered array // It does not alter the items array (and assumes that it doesn't during execution static filter<T>(items: Array<T>, fn: (item: T) => Promise<boolean>): Promise<Array<T>> { const results: Array<T> = []; return HigherOrderPromise.for(0, items.length, 1, i => fn(items[i]).then(result => { if (result) { results.push(items[i]); } })) .then(() => results); } } const whileLocal = (fnContinue: () => boolean, fn: () => Promise<any>, promise: DeferredPromise<any>) => { setImmediate(() => { try { if (fnContinue()) { fn() .then(() => whileLocal(fnContinue, fn, promise)) .catch(e => promise.reject(e)); } else { promise.resolve(undefined); } } catch (e) { promise.reject(e); } }); } const forEachWorker = <T, U>(items: Array<T>, fn: (item: T) => Promise<unknown>): Promise<unknown> => { if (items.length == 0) { return Promise.resolve(); } const head = items.shift(); try { return fn(head!) .then(() => forEachWorker(items, fn)); } catch (e) { return Promise.reject(e); } }