Running Effects
To execute an effect, you can use one of the many run
functions provided by the Effect
module.
The Effect.runSync
function executes an effect synchronously, meaning it runs immediately and returns the result.
Example (Synchronous Logging)
Effect.runSync
will throw an error if the effect fails or if it involves any asynchronous operations. In such cases, execution will stop at the point where the asynchronous task occurs:
Effect.runSyncExit
runs an effect synchronously, returning the result as an Exit, which represents the outcome (success or failure) of the effect.
Example (Handling Results as Exit)
If the effect involves any asynchronous operations, Effect.runSyncExit
will return an Exit with a Die
cause, indicating that the effect can’t be resolved synchronously.
Example (Asynchronous Operation Resulting in Die)
The Effect.runPromise
function is used to execute an effect and obtain the result as a Promise
.
Example (Running an Effect as a Promise)
If the effect fails, Effect.runPromise
will reject with an error:
The Effect.runPromiseExit
function runs an effect and returns a Promise
that resolves to an Exit, which represents the outcome (success or failure) of the effect.
Example (Handling Results as Exit)
The Effect.runFork
function is the foundational function for running effects.
It starts a fiber that can be observed or interrupted.
All other run functions are built on top of runFork
.
Example (Running an Effect with a Fiber)
In this example, the program
continuously logs “running…” with each repetition spaced 200 milliseconds apart. You can learn more about repetitions and scheduling in our Introduction to Scheduling guide.
To stop the execution of the program, we use Fiber.interrupt
on the fiber returned by Effect.runFork
. This allows you to control the execution flow and terminate it when necessary.
For a deeper understanding of how fibers work and how to handle interruptions, check out our guides on Fibers and Interruptions.
In the Effect library, there is no built-in way to determine in advance whether an effect will execute synchronously or asynchronously. While this idea was considered in earlier versions of Effect, it was ultimately not implemented for a few important reasons:
-
Complexity: Introducing this feature to track sync/async behavior in the type system would make Effect more complex to use and limit its composability.
-
Safety Concerns: We experimented with different approaches to track asynchronous Effects, but they all resulted in a worse developer experience without significantly improving safety. Even with fully synchronous types, we needed to support a
fromCallback
combinator to work with APIs using Continuation-Passing Style (CPS). However, at the type level, it’s impossible to guarantee that such a function is always called immediately and not deferred.
In most cases, effects are run at the outermost parts of your application. Typically, an application built around Effect will involve a single call to the main effect. Here’s how you should approach effect execution:
-
Use
runPromise
orrunFork
: For most cases, asynchronous execution should be the default. These methods provide the best way to handle Effect-based workflows. -
Use
runSync
only when necessary: Synchronous execution should be considered an edge case, used only in scenarios where asynchronous execution is not feasible. For example, when you are sure the effect is purely synchronous and need immediate results.
The table provides a summary of the available run*
functions, along with their input and output types, allowing you to choose the appropriate function based on your needs.
API | Given | Result |
---|---|---|
runSync | Effect<A, E> | A |
runSyncExit | Effect<A, E> | Exit<A, E> |
runPromise | Effect<A, E> | Promise<A> |
runPromiseExit | Effect<A, E> | Promise<Exit<A, E>> |
runFork | Effect<A, E> | RuntimeFiber<A, E> |
You can find the complete list of run*
functions here.