diff --git a/src/fire-and-forgetter.ts b/src/fire-and-forgetter.ts index af27d34..2555864 100644 --- a/src/fire-and-forgetter.ts +++ b/src/fire-and-forgetter.ts @@ -3,20 +3,20 @@ import ClosingError from "./errors/closing-error"; import TimeoutClosingError from "./errors/timeout-closing-error"; type Options = { - defaultOnError: (error: Error) => void; + defaultOnError: (error: Error) => void; }; type FireAndForgetter = { - close: (options?: CloseOptions) => Promise; + close: (options?: CloseOptions) => Promise; } & ((func: () => Promise, onError?: (error: Error) => void) => void); type CloseOptions = { - timeout: number; -} + timeout: number; +}; const defaultOptions: Options = { - defaultOnError: (error) => console.error(error), -} + defaultOnError: (error) => console.error(error), +}; /** * Get a new instance of the fire and forgetter lib. @@ -27,64 +27,72 @@ const defaultOptions: Options = { * }] * @returns a fire and forgetter object instance. */ -export function fireAndForgetter(options: Options = defaultOptions): FireAndForgetter { - - const counter = createCounter(); - let closing = false; +export function fireAndForgetter( + options: Options = defaultOptions +): FireAndForgetter { + const counter = createCounter(); + let closing = false; - async function executeFireAndForget(func: () => Promise): Promise { - try { - counter.incrementCounter(); - await func(); - } finally { - counter.decrementCounter(); - } + /** + * Execute a function in fire and forget mode. + * + * @param {() => Promise} func function executed in fire and forget mode. It must return a promise. + * @param {(error: Error) => void} [onError=options.defaultOnError] error callback to handle function rejection. + * @throws {ClosingError} when close function is called this error will be thrown. + */ + function fireAndForget( + func: () => Promise, + onError: (error: Error) => void = options.defaultOnError + ): void { + if (closing) { + throw new ClosingError( + "Cannot longer execute fire and forget operation as is closing or closed" + ); } + counter.incrementCounter(); + func() + .catch(onError) + .finally(() => counter.decrementCounter()); + } - /** - * Execute a function in fire and forget mode. - * - * @param {() => Promise} func function executed in fire and forget mode. It must return a promise. - * @param {(error: Error) => void} [onError=options.defaultOnError] error callback to handle function rejection. - * @throws {ClosingError} when close function is called this error will be thrown. - */ - function fireAndForget(func: () => Promise, onError: (error: Error) => void = options.defaultOnError): void { - if (closing) { - throw new ClosingError("Cannot longer execute fire and forget operation as is closing or closed"); + /** + * close the fire and forgetter instance. + * The function will return a promise that will resolve once all fire and forget operations are done. + * Also, any new fire and forget function requested will throw a ClosingError. + * + * @param {{ timeout: number }} [closeOptions={ timeout: 0 }] if timeout is > 0 the function + * will throw a TimeoutClosingError if fire and forget operations do not complete before the set timeout. + * default timeout value is 0, means no timeout. + * @returns {Promise} + * @throws {TimeoutClosingError} when fire and forget operations do not complete before the set timeout. + */ + function close(closeOptions: CloseOptions = { timeout: 0 }): Promise { + closing = true; + return new Promise((resolve, reject) => { + if (counter.getCount() === 0) { + resolve(); + return; + } + counter.registerSubscriberToCounterChanges((count) => { + if (count === 0) { + resolve(); } - executeFireAndForget(func).catch(onError); - } - - /** - * close the fire and forgetter instance. - * The function will return a promise that will resolve once all fire and forget operations are done. - * Also, any new fire and forget function requested will throw a ClosingError. - * - * @param {{ timeout: number }} [closeOptions={ timeout: 0 }] if timeout is > 0 the function - * will throw a TimeoutClosingError if fire and forget operations do not complete before the set timeout. - * default timeout value is 0, means no timeout. - * @returns {Promise} - * @throws {TimeoutClosingError} when fire and forget operations do not complete before the set timeout. - */ - function close(closeOptions: CloseOptions = { timeout: 0 }): Promise { - closing = true; - return new Promise((resolve, reject) => { - if (counter.getCount() === 0) { - resolve(); - return; - } - counter.registerSubscriberToCounterChanges((count) => { - if (count === 0) { - resolve(); - } - }); - const { timeout } = closeOptions; - if (timeout > 0) { - setTimeout(() => reject(new TimeoutClosingError(`Cannot close after ${timeout}ms, ${counter.getCount()} fire and forget operations are still in progress`)), timeout); - } - }); - } - fireAndForget.close = close; + }); + const { timeout } = closeOptions; + if (timeout > 0) { + setTimeout( + () => + reject( + new TimeoutClosingError( + `Cannot close after ${timeout}ms, ${counter.getCount()} fire and forget operations are still in progress` + ) + ), + timeout + ); + } + }); + } + fireAndForget.close = close; - return fireAndForget; + return fireAndForget; } diff --git a/test/index.spec.ts b/test/index.spec.ts index 9e42f3a..cd0f090 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -19,9 +19,7 @@ describe("fire-and-forgetter", () => { return Promise.resolve(); } - async function doSomeStuffsAndIncrementCountAtTheEndAndReject(): Promise< - void - > { + async function doSomeStuffsAndIncrementCountAtTheEndAndReject(): Promise { await setTimeout(10); count++; return Promise.reject(new Error("ups, some error happened")); @@ -39,7 +37,7 @@ describe("fire-and-forgetter", () => { test("close should throw a timeout closing error if timeout is reached and fire and forget operation are still in process", async () => { const fireAndForget = fireAndForgetter(); - + let functionHasThrownError = false; let count = 0; async function doSomeStuffsAndIncrementCountAtTheEnd(): Promise { @@ -62,7 +60,9 @@ describe("fire-and-forgetter", () => { "Cannot close after 10ms, 3 fire and forget operations are still in progress" ); equal(count, 0); + functionHasThrownError = true; } + equal(functionHasThrownError, true); }); test("close should resolve when no fire and forget operations are in process", async () => { @@ -71,8 +71,8 @@ describe("fire-and-forgetter", () => { }); test("fireAndForget should call onError callback when operation rejects", async () => { + let onErrorHasBeenCalled = false; const fireAndForget = fireAndForgetter(); - async function doSomeStuffsAndReject(): Promise { await setTimeout(10); return Promise.reject(new Error("ups, some error happened")); @@ -80,21 +80,25 @@ describe("fire-and-forgetter", () => { fireAndForget( () => doSomeStuffsAndReject(), - error => { + (error) => { equal(error instanceof Error, true); equal((error as Error).message, "ups, some error happened"); + onErrorHasBeenCalled = true; } ); await fireAndForget.close(); + equal(onErrorHasBeenCalled, true); }); test("fireAndForget should call defaultOnError callback when operation rejects and no onError callback is set", async () => { + let defaultOnErrorHasBeenCalled = false; const fireAndForget = fireAndForgetter({ - defaultOnError: error => { + defaultOnError: (error) => { equal(error instanceof Error, true); equal(error.message, "ups, some error happened"); - } + defaultOnErrorHasBeenCalled = true; + }, }); async function doSomeStuffsAndReject(): Promise { @@ -105,9 +109,12 @@ describe("fire-and-forgetter", () => { fireAndForget(() => doSomeStuffsAndReject()); await fireAndForget.close(); + + equal(defaultOnErrorHasBeenCalled, true); }); test("fireAndForget should throw a closing error if fire and forget has been closed", async () => { + let functionHasThrownError = false; const fireAndForget = fireAndForgetter(); async function doSomeStuffsAndIncrementCountAtTheEnd(): Promise { @@ -121,13 +128,15 @@ describe("fire-and-forgetter", () => { try { fireAndForget(() => doSomeStuffsAndIncrementCountAtTheEnd()); - throw new Error("It should not get here"); } catch (error) { equal(error instanceof ClosingError, true); equal( (error as Error).message, "Cannot longer execute fire and forget operation as is closing or closed" ); + functionHasThrownError = true; } + + equal(functionHasThrownError, true); }); });