feat: Add function promiseSome

Co-Authored-By: Ludovico Granata <Ludogranata@gmail.com>
This commit is contained in:
Simone Persiani 2021-08-16 10:58:28 +02:00 committed by Joachim Van Herwegen
parent c13c03ef54
commit 0355673a0f
3 changed files with 62 additions and 0 deletions

View File

@ -325,6 +325,7 @@ export * from './util/FetchUtil';
export * from './util/GuardedStream'; export * from './util/GuardedStream';
export * from './util/HeaderUtil'; export * from './util/HeaderUtil';
export * from './util/PathUtil'; export * from './util/PathUtil';
export * from './util/PromiseUtil';
export * from './util/QuadUtil'; export * from './util/QuadUtil';
export * from './util/RecordObject'; export * from './util/RecordObject';
export * from './util/ResourceUtil'; export * from './util/ResourceUtil';

30
src/util/PromiseUtil.ts Normal file
View File

@ -0,0 +1,30 @@
// eslint-disable-next-line @typescript-eslint/no-empty-function
const infinitePromise = new Promise<boolean>((): void => {});
/**
* A function that simulates the Array.some behaviour but on an array of Promises.
* Returns true if at least one promise returns true.
* Returns false if all promises return false or error.
*
* @remarks
*
* Predicates provided as input must be implemented considering
* the following points:
* 1. if they throw an error, it won't be propagated;
* 2. throwing an error should be logically equivalent to returning false.
*/
export async function promiseSome(predicates: Promise<boolean>[]): Promise<boolean> {
// These promises will only finish when their predicate returns true
const infinitePredicates = predicates.map(async(predicate): Promise<boolean> => predicate.then(
async(value): Promise<boolean> => value ? true : infinitePromise,
async(): Promise<boolean> => infinitePromise,
));
// Returns after all predicates are resolved
const finalPromise = Promise.allSettled(predicates).then((results): boolean =>
results.some((result): boolean => result.status === 'fulfilled' && result.value));
// Either one of the infinitePredicates will return true,
// or finalPromise will return the result if none of them did or finalPromise was faster
return Promise.race([ ...infinitePredicates, finalPromise ]);
}

View File

@ -0,0 +1,31 @@
import { promiseSome } from '../../../src/util/PromiseUtil';
describe('PromiseUtil', (): void => {
describe('#promiseSome', (): void => {
const resultTrue = Promise.resolve(true);
const resultFalse = Promise.resolve(false);
const resultError = Promise.reject(new Error('generic error'));
// eslint-disable-next-line @typescript-eslint/no-empty-function
const resultInfinite = new Promise<boolean>((): void => {});
it('returns false if no promise is provided.', async(): Promise<void> => {
await expect(promiseSome([])).resolves.toEqual(false);
});
it('returns false if no promise returns true.', async(): Promise<void> => {
await expect(promiseSome([ resultFalse, resultFalse, resultFalse ])).resolves.toEqual(false);
});
it('returns true if at least a promise returns true.', async(): Promise<void> => {
await expect(promiseSome([ resultFalse, resultTrue, resultFalse ])).resolves.toEqual(true);
});
it('does not propagate errors.', async(): Promise<void> => {
await expect(promiseSome([ resultError, resultFalse, resultFalse ])).resolves.toEqual(false);
});
it('works with a combination of promises.', async(): Promise<void> => {
await expect(promiseSome([ resultError, resultTrue, resultInfinite ])).resolves.toEqual(true);
});
});
});