Skip to content

Commit

Permalink
add Effect.timeoutOption (#2541)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Apr 30, 2024
1 parent a1c7ab8 commit 1c9454d
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 3 deletions.
18 changes: 18 additions & 0 deletions .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")
)
```
29 changes: 26 additions & 3 deletions packages/effect/src/Effect.ts
Expand Up @@ -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.
Expand All @@ -2946,6 +2945,30 @@ export const timeout: {
<A, E, R>(self: Effect<A, E, R>, duration: Duration.DurationInput): Effect<A, Cause.TimeoutException | E, R>
} = 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): <A, E, R>(self: Effect<A, E, R>) => Effect<Option.Option<A>, E, R>
<A, E, R>(self: Effect<A, E, R>, duration: Duration.DurationInput): Effect<Option.Option<A>, 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.
Expand Down
16 changes: 16 additions & 0 deletions packages/effect/src/internal/effect/circular.ts
Expand Up @@ -467,6 +467,22 @@ export const timeoutFailCause = dual<
duration
})))

/** @internal */
export const timeoutOption = dual<
(
duration: Duration.DurationInput
) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<Option.Option<A>, E, R>,
<A, E, R>(
self: Effect.Effect<A, E, R>,
duration: Duration.DurationInput
) => Effect.Effect<Option.Option<A>, E, R>
>(2, (self, duration) =>
timeoutTo(self, {
duration,
onSuccess: Option.some,
onTimeout: Option.none
}))

/** @internal */
export const timeoutTo = dual<
<A, B, B1>(
Expand Down

0 comments on commit 1c9454d

Please sign in to comment.