diff --git a/README.md b/README.md index 35914cb..83570fa 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,23 @@ const promise = anAsyncCall(); const [error, result] = await tryCatch(promise); ``` +#### [Try One By One](https://thalesrc.github.io/js-utils/modules/_promise_try_one_by_one_.html) +Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. + +```typescript +import { tryOneByOne } from "@thalesrc/js-utils/promise"; + +async function fooFunction() { + const foo = await tryOneByOne([ + () => someCall(), + (err) => anotherCall(), + (err) => fooPromise() + ]); + + // do stuff +} +``` + ### String #### [Limit](https://thalesrc.github.io/js-utils/modules/_string_limit_.html) diff --git a/src/promise/index.ts b/src/promise/index.ts index 5ec29ac..5343845 100644 --- a/src/promise/index.ts +++ b/src/promise/index.ts @@ -1,4 +1,5 @@ export * from './never'; export * from './revert'; export * from './try-catch'; +export * from './try-one-by-one'; export * from './timeout'; diff --git a/src/promise/static/try-one-by-one.ts b/src/promise/static/try-one-by-one.ts new file mode 100644 index 0000000..e83a36e --- /dev/null +++ b/src/promise/static/try-one-by-one.ts @@ -0,0 +1,30 @@ +import { tryOneByOne } from '../try-one-by-one'; + +declare global { + export interface PromiseConstructor { + /** + * #### Try One By One + * Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. + * + * * * * + * Example: + * ```typescript + * import "@thalesrc/js-utils/promise/static/try-one-by-one"; + * + * async function fooFunction() { + * const foo = await Promise.tryOneByOne([ + * () => someCall(), + * (err) => anotherCall(), + * (err) => fooPromise() + * ]); + * + * // do stuff + * } + * ``` + * * * * + */ + tryOneByOne: typeof tryOneByOne; + } +} + +Promise.tryOneByOne = tryOneByOne; diff --git a/src/promise/try-one-by-one.spec.ts b/src/promise/try-one-by-one.spec.ts new file mode 100644 index 0000000..079afbb --- /dev/null +++ b/src/promise/try-one-by-one.spec.ts @@ -0,0 +1,34 @@ +import 'jest'; + +import { tryOneByOne } from './try-one-by-one'; + +class CustomError { + constructor(public message: string) {} +} + +describe('TryOneByOne Function', () => { + it('should resolve if a promise resolved', done => { + tryOneByOne([Promise.resolve('foo')]) + .then(res => { + expect(res).toBe('foo'); + done(); + }) + .catch(err => { + throw new CustomError("didn't resolve"); + }); + }); + + it('should try the next promise when the first threw error', done => { + tryOneByOne([ + Promise.reject('foo'), + Promise.resolve('bar'), + ]) + .then(res => { + expect(res).toBe('bar'); + done(); + }) + .catch(err => { + throw new CustomError("didn't resolve"); + }); + }); +}); diff --git a/src/promise/try-one-by-one.ts b/src/promise/try-one-by-one.ts new file mode 100644 index 0000000..718d279 --- /dev/null +++ b/src/promise/try-one-by-one.ts @@ -0,0 +1,58 @@ +/** + * #### Try One By One + * + * Tries a set of promises one by one with given order. Breaks the call when a promise resolved. Otherwise keeps trying incoming promises until the list is finished. + * + * * * * + * Example: + * ```typescript + * import { tryOneByOne } from "@thalesrc/js-utils/promise"; + * + * async function fooFunction() { + * const foo = await tryOneByOne([ + * () => someCall(), + * (err) => anotherCall(), + * (err) => fooPromise() + * ]); + * + * // do stuff + * } + * + * ``` + * + * Static Example: + * ```typescript + * import "@thalesrc/js-utils/promise/static/try-one-by-one"; + * + * async function fooFunction() { + * const foo = await Promise.tryOneByOne([ + * () => someCall(), + * (err) => anotherCall(), + * (err) => fooPromise() + * ]); + * + * // do stuff + * } + * + * ``` + * * * * + * @param promises List of promises to try one by one with the order + * @returns + */ +export async function tryOneByOne(promises: Array | ((lastError: unknown) => Promise)>): Promise { + return await tryPromises([...promises], null); +} + +async function tryPromises(promises: Array | ((lastError: unknown) => Promise)>, lastError: unknown): Promise { + const promiseFn = promises.shift(); + + if (!promiseFn) throw lastError; + + try { + const promise = typeof promiseFn === "function" ? promiseFn(lastError) : promiseFn; + + return await promise; + } catch (err) { + return tryPromises(promises, err); + } +}