UNPKG

prutill

Version:

Environment-agnostic production-ready promise utility library for managing promise stacks and race conditions. Supports Node.js, Deno, and browsers.

146 lines (130 loc) 4.61 kB
/** * A builder of promise wrapper function which acts based on lastly added promise. * * By default, previously added promises are resolved with the lastly added value, * so any previously added promise is resolved with latest value. * This may be changed by setting `resolveAllPrevious` to false. * * This solves the problem where multiple same promises are made, * but previously added didn't resolve yet or some previous may be quicker. * Especially for React where we creating promises in useEffect hook, * but we want to act on single last promise. * * Example of usage: * ``` * // it build the single stack function wrapper to stack promises * const lastPromiseReactor = stackBuilder(); * * // PromiseX is added to the stack and then function acts on it, * // once the lastPromiseReactor is called again with different promise, * // "then" function will act on the lastly added one * lastPromiseReactor(...PromiseX...).then(...); * ``` * * @returns A single stack function wrapper. */ export function stackBuilder<T>(resolveAllPrevious = true): (_: Promise<T>) => Promise<T> { let storedResolveCallbacks: ((_value: T) => void)[] = []; let storedPromises: Promise<T>[] = []; return promise => { storedPromises.push(promise); const promiseWithId = { id: storedPromises.length, promise, }; return new Promise((resolve, reject) => { if (resolveAllPrevious) { storedResolveCallbacks.push(resolve); } // act on any promise promiseWithId.promise.then(result => { // act on the last promise if (promiseWithId.id === storedPromises.length) { if (resolveAllPrevious) { for (let i = 0; i < storedResolveCallbacks.length; i++) { storedResolveCallbacks[i]!(result); } } else { resolve(result); } storedPromises = []; storedResolveCallbacks = []; } }, reject); }); }; } const storedPromiseStacks: Record<string, (_: Promise<unknown>) => Promise<unknown>> = {}; /** * Provides builded stackBuilder tied to unique key. * * @returns A single stack function wrapper by unique key */ export function getLastPromise<T>(key: string, promise: Promise<T>, resolveAllPrevious = true): Promise<T> { if (storedPromiseStacks[key] === undefined) { storedPromiseStacks[key] = stackBuilder<T>(resolveAllPrevious) as (_: Promise<unknown>) => Promise<unknown>; } return storedPromiseStacks[key](promise) as Promise<T>; } /** * A builder of promise wrapper function which acts acording to first finished promise. * * By default, previously added promises are resolved with the first resolved value, * so any previously added promise is resolved with this value as well. * This may be changed by setting `resolveAllOthers` to false. * * This solves the problem where multiple same promises are made and some may be quicker. * We suppose that first finished promise talk for all added ones and the rest will be finished with same value. * * Example of usage: * ``` * // it build the single stack function wrapper to stack promises * const promiseInRace = raceBuilder(); * * // PromiseX is added to the stack and then function acts on it, * // once the promiseInRace is called again with different promise, * // "then" function will act based on first finished promise * promiseInRace(...PromiseX...).then(...); * ``` * * @returns A single stack function wrapper. */ export function raceBuilder<T>(resolveAllOthers = true): (_: Promise<T>) => Promise<T> { let storedResolveCallbacks: ((_value: T) => void)[] = []; let resolving = false; return promise => { return new Promise(resolve => { if (resolveAllOthers) { storedResolveCallbacks.push(resolve); } promise.then(result => { if (resolving) { return; } else { resolving = true; } if (resolveAllOthers) { for (let i = 0; i < storedResolveCallbacks.length; i++) { storedResolveCallbacks[i]!(result); } } else { resolve(result); } storedResolveCallbacks = []; resolving = false; }); }); }; } const storedPromiseRaces: Record<string, (_: Promise<unknown>) => Promise<unknown>> = {}; /** * Provides builded raceBuilder tied to unique key. * * @returns A single stack function wrapper by unique key */ export function getRaceWonPromise<T>(key: string, promise: Promise<T>, resolveAllOthers = true): Promise<T> { if (storedPromiseRaces[key] === undefined) { storedPromiseRaces[key] = raceBuilder<T>(resolveAllOthers) as (_: Promise<unknown>) => Promise<unknown>; } return storedPromiseRaces[key](promise) as Promise<T>; }