From c5cd47a5356d682887318fdcaedd4fef071decfd Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 17 Apr 2024 13:19:54 +1200 Subject: [PATCH] add Effect.timeoutOption (#2541) --- .changeset/poor-fans-change.md | 18 ++++++++++++ packages/effect/src/Effect.ts | 29 +++++++++++++++++-- .../effect/src/internal/effect/circular.ts | 16 ++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 .changeset/poor-fans-change.md 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 1f0b933c5c..c0cc8dfe30 100644 --- a/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -2931,9 +2931,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. @@ -2954,6 +2953,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< (