attempt
The attempt utility safely executes a function and returns a tuple containing either the result or the error. It follows the "Go-style" error handling pattern, allowing you to handle failures without try/catch blocks.
Implementation
View Source Code
ts
import { Logit } from '@vielzeug/logit';
import type { Fn } from '../types';
import { predict } from './predict';
import { retry } from './retry';
type AttemptOptions = {
identifier?: string;
retries?: number;
silent?: boolean;
timeout?: number;
};
/**
* Attempts to execute a function with advanced error handling and retry logic.
*
* @example
* ```ts
* const unreliableFunction = async () => {
* if (Math.random() < 0.7) throw new Error ('Random failure');
* return 'Success!';
* };
*
* await attempt(
* unreliableFunction,
* { retries: 3, silent: false, timeout: 5000 }); // Success! (or undefined if all attempts failed)
* ```
*
* @param fn - The function to be executed.
* @param [options] - Configuration options for the attempt.
* @param [options.identifier] - Custom identifier for logging purposes.
* @param [options.retries=0] - Number of retry attempts if the function fails.
* @param [options.silent=false] - If true, suppresses error logging.
* @param [options.timeout=7000] - Timeout in milliseconds for function execution.
*
* @returns The result of the function or undefined if it failed.
*/
export async function attempt<T extends Fn, R = Awaited<ReturnType<T>>>(
fn: T,
{ silent = false, retries = 0, timeout = 7000, identifier = fn.name || 'anonymous function' }: AttemptOptions = {},
): Promise<R | undefined> {
try {
return await retry(() => predict<R>(() => fn(), { timeout }), { times: retries + 1 });
} catch (err) {
if (!silent) {
Logit.error(`attempt(${identifier}) -> all attempts failed`, { cause: err });
}
return undefined;
}
}Features
- Isomorphic: Works in both Browser and Node.js.
- Async Support: Automatically handles Promises. If the input function returns a Promise,
attemptreturns a Promise resolving to the result/error tuple. - Clean Syntax: Encourages a consistent, flat error-handling style across your codebase.
- Type-safe: Correctly typed result and error positions.
API
ts
type AttemptResult<T> = [T, null] | [null, any];
function attempt<T>(fn: () => T | Promise<T>): Promise<AttemptResult<T>>;Parameters
fn: The function to execute. Can be synchronous or asynchronous.
Returns
- A tuple:
[result, null]on success, or[null, error]on failure. - Returns a Promise resolving to this tuple if
fnis asynchronous.
Examples
Synchronous Error Handling
ts
import { attempt } from '@vielzeug/toolkit';
const [data, error] = attempt(() => JSON.parse('invalid json'));
if (error) {
console.error('Failed to parse:', error.message);
} else {
console.log('Parsed data:', data);
}Asynchronous Operations
ts
import { attempt } from '@vielzeug/toolkit';
const [user, fetchError] = await attempt(() => fetch('/api/user/1').then((r) => r.json()));
if (fetchError) {
handleError(fetchError);
}Implementation Notes
- Performance-optimized with minimal overhead compared to a raw
try/catch. - If the argument
fnis not a function, it returns[null, TypeError](depending on implementation, usually it expects a function). - Works perfectly with
awaitfor a clean, readable async flow.
See Also
- retry: Automatically re-run logic that might fail.
- assert: Throw errors when conditions are not met.
- parseJSON: Specialized safe JSON parsing.