Skip to content

Commit cecf0f5

Browse files
authored
add finally (#14)
* ✨ add finally callback to Try class - Implemented `finally` method to allow users to register a callback that runs after the wrapped function finishes, regardless of success or failure. - Updated README to include usage example for the new feature. - Added tests to verify the execution of the `finally` callback on both successful and erroneous function executions. Signed-off-by: w01fgang <sumin@unix-center.ru> * 🔖 bump version to 0.0.7 Signed-off-by: w01fgang <sumin@unix-center.ru> * Remove unused `finallyExecuted` property from Try class to streamline error handling logic. Signed-off-by: w01fgang <sumin@unix-center.ru> * Refactor test for Try class to use `unwrap` method and validate error handling - Updated the test case to utilize the `unwrap` method instead of `value`. - Adjusted the expectation to check for rejection with the correct error message. Signed-off-by: w01fgang <sumin@unix-center.ru> --------- Signed-off-by: w01fgang <sumin@unix-center.ru>
1 parent 4a08a96 commit cecf0f5

4 files changed

Lines changed: 57 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ const user = await new Try(fetchUser, userId)
110110
.report('Failed to fetch user')
111111
.breadcrumbs(['userId'])
112112
.default(null)
113+
.finally(() => {
114+
console.log('Completed fetching user')
115+
})
113116
.value();
114117

115118
// Pattern 2: Check errors explicitly

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@power-rent/try-catch",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"description": "A TypeScript utility for simplified async error handling with Sentry integration",
55
"main": "dist/index.js",
66
"module": "dist/esm/index.js",

src/__tests__/Try.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,26 @@ describe('Try', () => {
228228
expect(() => result.ok).not.toThrow(TypeError);
229229
expect(Sentry.captureException).not.toHaveBeenCalled();
230230
});
231+
232+
it('should execute finally callback on success', async () => {
233+
const params = { parameterKey: 'alpha' };
234+
const finallySpy = vi.fn();
235+
236+
await new Try(successfulFunction, params)
237+
.finally(finallySpy)
238+
.unwrap();
239+
240+
expect(finallySpy).toHaveBeenCalledTimes(1);
241+
});
242+
243+
it('should execute finally callback on error', async () => {
244+
const params = { parameterKey: 'alpha' };
245+
const finallySpy = vi.fn();
246+
247+
const exec = new Try(throwingFunction, params)
248+
.finally(finallySpy)
249+
.unwrap();
250+
await expect(exec).rejects.toThrow('boom');
251+
expect(finallySpy).toHaveBeenCalledTimes(1);
252+
});
231253
});

src/nextjs/index.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ interface TryConfig<TArg extends Record<string, any>> {
88
readonly breadcrumbKeys?: readonly (keyof TArg)[];
99
readonly tags: Readonly<Record<string, string>>;
1010
readonly defaultValue?: unknown;
11+
/**
12+
* Callback that will always run after the wrapped function finishes
13+
* executing, regardless of success or failure. Similar to `Promise.prototype.finally`.
14+
*/
15+
readonly finallyCallback?: () => void;
1116
}
1217

1318
/**
@@ -180,6 +185,23 @@ export class Try<T, TArgs extends readonly Record<string, any>[] = Record<string
180185
});
181186
}
182187

188+
/**
189+
* Register a callback that will run after the wrapped function finishes
190+
* executing (successfully or with an error). The callback runs exactly once
191+
* per {@link Try} instance, mirroring the behaviour of
192+
* `Promise.prototype.finally`.
193+
*
194+
* The callback is executed **after** the underlying function settles but
195+
* before any error is re-thrown from {@link unwrap}. It is always executed
196+
* asynchronously in the same tick as the function resolution.
197+
*
198+
* @param callback A function to invoke once the wrapped operation settles.
199+
* @returns The `Try` instance for method chaining.
200+
*/
201+
finally(callback: () => void): Try<T, TArgs> {
202+
return this.setConfig({ finallyCallback: callback });
203+
}
204+
183205
/**
184206
* Configure a default value to return when an error occurs.
185207
* This default value will be returned by `.value()` method if the function execution fails.
@@ -352,14 +374,21 @@ export class Try<T, TArgs extends readonly Record<string, any>[] = Record<string
352374

353375
try {
354376
const value = await this.fn(...this.args);
355-
return { success: true, value };
377+
this.result = { success: true, value };
356378
} catch (e) {
357379
console.error(e);
358380
const error = e as Error;
359-
return { success: false, error };
381+
this.result = { success: false, error };
360382
} finally {
361383
this.state = 'executed';
384+
try {
385+
this.config.finallyCallback?.();
386+
} catch (err) {
387+
console.error('Error in finally callback', err);
388+
}
362389
}
390+
391+
return this.result;
363392
}
364393

365394
/**

0 commit comments

Comments
 (0)