From 5bc3dd5ee00bc16e74599b906c0eead93499b624 Mon Sep 17 00:00:00 2001 From: robonen Date: Thu, 24 Oct 2024 07:29:55 +0700 Subject: [PATCH] feat(packages/stdlib): add tryIt async util --- packages/stdlib/src/async/tryIt/index.test.ts | 67 +++++++++++++++++++ packages/stdlib/src/async/tryIt/index.ts | 41 ++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 packages/stdlib/src/async/tryIt/index.test.ts create mode 100644 packages/stdlib/src/async/tryIt/index.ts diff --git a/packages/stdlib/src/async/tryIt/index.test.ts b/packages/stdlib/src/async/tryIt/index.test.ts new file mode 100644 index 0000000..c84da67 --- /dev/null +++ b/packages/stdlib/src/async/tryIt/index.test.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest'; +import { tryIt } from '.'; + +describe('tryIt', () => { + it('handle synchronous functions without errors', () => { + const syncFn = (x: number) => x * 2; + const wrappedSyncFn = tryIt(syncFn); + + const [error, result] = wrappedSyncFn(2); + + expect(error).toBeUndefined(); + expect(result).toBe(4); + }); + + it('handle synchronous functions with errors', () => { + const syncFn = (): void => { throw new Error('Test error') }; + const wrappedSyncFn = tryIt(syncFn); + + const [error, result] = wrappedSyncFn(); + + expect(error).toBeInstanceOf(Error); + expect(error?.message).toBe('Test error'); + expect(result).toBeUndefined(); + }); + + it('handle asynchronous functions without errors', async () => { + const asyncFn = async (x: number) => x * 2; + const wrappedAsyncFn = tryIt(asyncFn); + + const [error, result] = await wrappedAsyncFn(2); + + expect(error).toBeUndefined(); + expect(result).toBe(4); + }); + + it('handle asynchronous functions with errors', async () => { + const asyncFn = async () => { throw new Error('Test error') }; + const wrappedAsyncFn = tryIt(asyncFn); + + const [error, result] = await wrappedAsyncFn(); + + expect(error).toBeInstanceOf(Error); + expect(error?.message).toBe('Test error'); + expect(result).toBeUndefined(); + }); + + it('handle promise-based functions without errors', async () => { + const promiseFn = (x: number) => Promise.resolve(x * 2); + const wrappedPromiseFn = tryIt(promiseFn); + + const [error, result] = await wrappedPromiseFn(2); + + expect(error).toBeUndefined(); + expect(result).toBe(4); + }); + + it('handle promise-based functions with errors', async () => { + const promiseFn = () => Promise.reject(new Error('Test error')); + const wrappedPromiseFn = tryIt(promiseFn); + + const [error, result] = await wrappedPromiseFn(); + + expect(error).toBeInstanceOf(Error); + expect(error?.message).toBe('Test error'); + expect(result).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/packages/stdlib/src/async/tryIt/index.ts b/packages/stdlib/src/async/tryIt/index.ts new file mode 100644 index 0000000..9b4d9ba --- /dev/null +++ b/packages/stdlib/src/async/tryIt/index.ts @@ -0,0 +1,41 @@ +import { isPromise } from '../../types'; + +export type TryItReturn = Return extends Promise + ? Promise<[Error, undefined] | [undefined, Awaited]> + : [Error, undefined] | [undefined, Return]; + +/** + * @name tryIt + * @category Async + * @description Wraps promise-based code in a try/catch block without forking the control flow + * + * @param {Function} fn - The function to try + * @returns {Function} - The function that will return a tuple with the error and the result + * + * @example + * const wrappedFetch = tryIt(fetch); + * const [error, result] = await wrappedFetch('https://jsonplaceholder.typicode.com/todos/1'); + * + * @example + * const [error, result] = await tryIt(fetch)('https://jsonplaceholder.typicode.com/todos/1'); + * + * @since 0.0.3 + */ +export function tryIt( + fn: (...args: Args) => Return, +) { + return (...args: Args): TryItReturn => { + try { + const result = fn(...args); + + if (isPromise(result)) + return result + .then((value) => [undefined, value]) + .catch((error) => [error, undefined]) as TryItReturn; + + return [undefined, result] as TryItReturn; + } catch (error) { + return [error, undefined] as TryItReturn; + } + }; +}