diff --git a/.changeset/poor-fans-change.md b/.changeset/poor-fans-change.md new file mode 100644 index 0000000000..18cd474157 --- /dev/null +++ b/.changeset/poor-fans-change.md @@ -0,0 +1,18 @@ +--- +"effect": minor +--- + +add Effect.timeoutOption + +Returns an effect that will return `None` if the effect times out, otherwise it +will return `Some` of the produced value. + +```ts +import { Effect } from "effect" + +// will return `None` after 500 millis +Effect.succeed("hello").pipe( + Effect.delay(1000), + Effect.timeoutOption("500 millis") +) +``` diff --git a/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts index 35484423b4..c5ce1221d5 100644 --- a/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -2923,9 +2923,8 @@ export const timedWith: { } = effect.timedWith /** - * Returns an effect that will timeout this effect, returning `None` if the - * timeout elapses before the effect has produced a value; and returning - * `Some` of the produced value otherwise. + * Returns an effect that will timeout this effect, failing with a `Cause.TimeoutException` + * if the timeout elapses before the effect has produced a value. * * If the timeout elapses without producing a value, the running effect will * be safely interrupted. @@ -2946,6 +2945,30 @@ export const timeout: { (self: Effect, duration: Duration.DurationInput): Effect } = circular.timeout +/** + * Returns an effect that will timeout this effect, returning `None` if the + * timeout elapses before the effect has produced a value; and returning + * `Some` of the produced value otherwise. + * + * If the timeout elapses without producing a value, the running effect will + * be safely interrupted. + * + * WARNING: The effect returned by this method will not itself return until + * the underlying effect is actually interrupted. This leads to more + * predictable resource utilization. If early return is desired, then instead + * of using `effect.timeout(d)`, use `effect.disconnect.timeout(d)`, which + * first disconnects the effect's interruption signal before performing the + * timeout, resulting in earliest possible return, before an underlying effect + * has been successfully interrupted. + * + * @since 3.1.0 + * @category delays & timeouts + */ +export const timeoutOption: { + (duration: Duration.DurationInput): (self: Effect) => Effect, E, R> + (self: Effect, duration: Duration.DurationInput): Effect, E, R> +} = circular.timeoutOption + /** * The same as `timeout`, but instead of producing a `None` in the event of * timeout, it will produce the specified error. diff --git a/packages/effect/src/internal/effect/circular.ts b/packages/effect/src/internal/effect/circular.ts index 1bfcb66712..d98904a53b 100644 --- a/packages/effect/src/internal/effect/circular.ts +++ b/packages/effect/src/internal/effect/circular.ts @@ -467,6 +467,22 @@ export const timeoutFailCause = dual< duration }))) +/** @internal */ +export const timeoutOption = dual< + ( + duration: Duration.DurationInput + ) => (self: Effect.Effect) => Effect.Effect, E, R>, + ( + self: Effect.Effect, + duration: Duration.DurationInput + ) => Effect.Effect, E, R> +>(2, (self, duration) => + timeoutTo(self, { + duration, + onSuccess: Option.some, + onTimeout: Option.none + })) + /** @internal */ export const timeoutTo = dual< (