diff --git a/.github/next-minor.md b/.github/next-minor.md index 2b8355528..f143e96db 100644 --- a/.github/next-minor.md +++ b/.github/next-minor.md @@ -4,7 +4,9 @@ The `####` headline should be short and descriptive of the new functionality. In ## New Functions -#### +#### `unawaited` + +https://github.com/radashi-org/radashi/pull/464 ## New Features diff --git a/benchmarks/async/unawaited.bench.ts b/benchmarks/async/unawaited.bench.ts new file mode 100644 index 000000000..321a9554e --- /dev/null +++ b/benchmarks/async/unawaited.bench.ts @@ -0,0 +1,9 @@ +import * as _ from 'radashi' +import { bench } from 'vitest' + +describe('unawaited', () => { + const promise = Promise.resolve() + bench('unawaited', () => { + _.unawaited(promise) + }) +}) diff --git a/cspell.yaml b/cspell.yaml index 4502a1f81..7bc957b10 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -48,6 +48,7 @@ words: - smidge - supabase - tryit + - unawaited - upperize - upserting - urlencode diff --git a/docs/async/unawaited.mdx b/docs/async/unawaited.mdx new file mode 100644 index 000000000..089a03f1c --- /dev/null +++ b/docs/async/unawaited.mdx @@ -0,0 +1,18 @@ +--- +title: unawaited +description: Attaches console.error to a given promise +--- + +### Usage + +Pass a promise to `unawaited` to ensure that any rejection is logged to the console instead of resulting in an unhandled promise rejection. + +```ts +import * as _ from 'radashi' + +async function someAsyncFunction() { + throw new Error('Something went wrong') +} + +_.unawaited(someAsyncFunction()) +``` diff --git a/src/async/unawaited.ts b/src/async/unawaited.ts new file mode 100644 index 000000000..7d7e2f8bb --- /dev/null +++ b/src/async/unawaited.ts @@ -0,0 +1,17 @@ +declare const console: { + error: (...args: any[]) => void +} + +/** + * Attaches a rejection handler that logs to console.error. + * + * @see https://radashi.js.org/reference/async/unawaited + * @example + * ```ts + * unawaited(asyncFunction()) + * ``` + * @version 12.3.0 + */ +export function unawaited(promise: Promise): void { + promise.catch(console.error) +} diff --git a/src/mod.ts b/src/mod.ts index de7f13d63..bd59cf2c4 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -46,6 +46,7 @@ export * from './async/sleep.ts' export * from './async/timeout.ts' export * from './async/toResult.ts' export * from './async/tryit.ts' +export * from './async/unawaited.ts' export * from './async/withResolvers.ts' export * from './curry/callable.ts' diff --git a/tests/async/unawaited.test.ts b/tests/async/unawaited.test.ts new file mode 100644 index 000000000..2678beea6 --- /dev/null +++ b/tests/async/unawaited.test.ts @@ -0,0 +1,18 @@ +import * as _ from 'radashi' + +describe('unawaited', () => { + test('returns undefined', () => { + expect(_.unawaited(Promise.resolve())).toBe(undefined) + }) + + test('calls console.error when promise rejects', async () => { + const error = new Error('test') + const spy = vi.spyOn(console, 'error').mockImplementation(() => {}) + const promise = Promise.reject(error) + _.unawaited(promise) + // We need to wait for the next tick to ensure the catch handler has been called + await new Promise(resolve => setTimeout(resolve, 0)) + expect(spy).toHaveBeenCalledWith(error) + spy.mockRestore() + }) +})