You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat!: remove thenable behavior from Try instances
Try is no longer thenable for any wrapped function. Callers must use
.value()/.unwrap()/.error()/.result() to execute.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove thenable behavior from `Try` instances entirely. No `Try` instance is ever thenable — `await new Try(fn)` yields the `Try` instance itself regardless of whether the wrapped function is sync or async. Callers must use `.value()`, `.unwrap()`, `.error()`, or `.result()` to execute and read the result. Migration: replace `await new Try(asyncFn, ...args)` with `await new Try(asyncFn, ...args).value()` (or `.unwrap()` / `.result()` / `.error()` depending on desired semantics).
Copy file name to clipboardExpand all lines: README.md
+8-12Lines changed: 8 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,9 +65,9 @@ import { Try } from '@power-rent/try-catch';
65
65
66
66
## Sync vs Async
67
67
68
-
Only `async` functions (declared with the `async` keyword) produce a thenable`Try` instance. Everything else — sync functions, *and sync functions that happen to return a Promise* — must be consumed via a terminal method.
68
+
`Try` instances are never thenable. Whether the wrapped function is sync or async, you must call a terminal method (`.value()`, `.unwrap()`, `.error()`, or `.result()`) to run it and read the outcome.
69
69
70
-
**Async functions** — `await` the terminal method (or the `Try` instance directly):
70
+
**Async functions** — `await` the terminal method:
71
71
72
72
```ts doctest
73
73
import { Try } from'@power-rent/try-catch';
@@ -76,14 +76,10 @@ async function asyncFn(arg: number) {
76
76
returnarg*2;
77
77
}
78
78
79
-
// Async function: await the terminal call
80
79
const result =awaitnewTry(asyncFn, 21).value();
81
80
82
-
// Or await the Try instance itself (works for async functions only)
Copy file name to clipboardExpand all lines: docs/ARCHITECTURE.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -86,13 +86,15 @@ When `.report()` is **not** configured but `.breadcrumbs()` is, each terminal me
86
86
87
87
## Sync vs async execution paths
88
88
89
-
The library resolves the sync/async split at construction time using a single check:
89
+
The library resolves the sync/async split at terminal-method call time. `Try` instances are **never thenable** — no `.then` is ever installed — so `await new Try(fn)` always yields the `Try` instance itself without triggering execution. Any thenability probe (`Promise.resolve`, `util.inspect`, deep-equality matchers, serializers) is guaranteed not to invoke the wrapped function.
90
+
91
+
Callers consume the result with `.value()`, `.unwrap()`, `.result()`, or `.error()`. Each terminal routes through `execute()`:
90
92
91
93
**Async path (declared `async` functions)**
92
-
`fn.constructor.name === 'AsyncFunction'` is true. `installThenable()` defines `.then` as an owned data property immediately, so `await new Try(asyncFn)` works without executing the function early.
94
+
`fn.constructor.name === 'AsyncFunction'` is true. The terminal returns a `Promise<...>` that callers `await`.
93
95
94
96
**Non-async path (everything else)**
95
-
No `.then` property is installed. The instance is **not thenable**, so `await new Try(nonAsyncFn)` yields the `Try` instance itself rather than triggering execution. This holds even when the wrapped function happens to return a `Promise` — any thenability probe (`Promise.resolve`, `util.inspect`, deep-equality matchers, serializers) is guaranteed not to invoke the wrapped function. Callers must use `.value()`, `.unwrap()`, `.result()`, or `.error()` directly. Each terminal routes through `execute()`, which detects a `Promise` return value and returns a `Promise<TryResult>` so awaiting the terminal still works.
97
+
The terminal runs synchronously. If the wrapped function happens to return a `Promise`, `execute()`detects it and returns a `Promise<TryResult>` so `await` still works on the terminal call.
96
98
97
99
Both paths cache the result in `this.exec` so that subsequent terminal method calls return the same settled value.
Only `async` functions (declared with the `async` keyword) produce a thenable`Try` instance. Everything else — sync functions, *and sync functions that happen to return a Promise* — must be consumed via a terminal method.
99
+
`Try` instances are never thenable. Whether the wrapped function is sync or async, you must call a terminal method (`.value()`, `.unwrap()`, `.error()`, or `.result()`) to run it and read the outcome.
100
100
101
-
**Async functions** — `await` the terminal method (or the `Try` instance directly):
101
+
**Async functions** — `await` the terminal method:
102
102
103
103
```ts doctest
104
104
import { Try } from'@power-rent/try-catch';
@@ -107,14 +107,10 @@ async function asyncFn(arg: number) {
107
107
returnarg*2;
108
108
}
109
109
110
-
// Async function: await the terminal call
111
110
const result =awaitnewTry(asyncFn, 21).value();
112
111
113
-
// Or await the Try instance itself (works for async functions only)
@@ -134,7 +130,7 @@ function returnsPromise(): Promise<number> {
134
130
}
135
131
const n =awaitnewTry(returnsPromise).value();
136
132
137
-
// Awaiting a non-async Try yields the Try instance, NOT the result.
133
+
// Awaiting a Try instance directly yields the Try instance, NOT the result.
138
134
// The instance is not thenable, so `await` cannot trigger execution.
139
135
// Use .value() / .unwrap() / .error() / .result() instead.
140
136
if ((resultas { ok:boolean }).ok!==true||n!==42) {
@@ -163,7 +159,7 @@ npm install @sentry/nextjs
163
159
164
160
Supported version range: `>=8.0.0 <11.0.0`.
165
161
166
-
**`await new Try(syncFn)` returns the `Try` instance** — This is expected behaviour. Awaiting a `Try` that wraps a non-async function (sync, or sync-returning-Promise) yields the `Try`instance itself because the instance is not thenable. Use `.value()`, `.unwrap()`, `.error()`, or `.result()` to retrieve the result.
162
+
**`await new Try(fn)` returns the `Try` instance** — This is expected behaviour for every wrapped function (sync or async). `Try`instances are not thenable, so `await` cannot trigger execution. Use `.value()`, `.unwrap()`, `.error()`, or `.result()` to retrieve the result.
0 commit comments