# Running Effects

An effect is inert until you run it. The `Effect` module provides a small family
of `run*` functions that take an `Effect<A, E>` and actually execute it on a
fiber. They differ in two ways: whether they return a value, a `Promise`, or a
`Fiber`; and whether they surface failures by throwing/rejecting or by handing
you a structured [`Exit`](https://effect.plants.sh/data-types/).

Each `run*` function requires an `Effect<A, E, never>` — the requirements
channel must be empty. Provide any [services and layers](https://effect.plants.sh/services-and-layers/)
the program needs *before* you run it.

```ts
import { Effect } from "effect"

const program = Effect.gen(function*() {
  yield* Effect.sleep("10 millis")
  return "done"
})

// Returns a Promise<string>. The default choice for async programs.
Effect.runPromise(program).then(console.log)
// Output: done
```

## runPromise

`runPromise` executes an effect and returns a `Promise<A>`. If the effect
succeeds, the promise resolves with the value; if it fails, the promise rejects.
This is the bridge to any promise-based world — top-level `await`, a test
runner, an existing async function.

```ts
import { Effect } from "effect"

// Success resolves the promise.
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

// Failure rejects the promise with a FiberFailure wrapping the cause.
Effect.runPromise(Effect.fail("boom")).catch(console.error)
// Output: (FiberFailure) Error: boom
```

## runFork

`runFork` is the foundational runner: it starts the effect on a background fiber
and immediately returns a [`Fiber<A, E>`](https://effect.plants.sh/concurrency/) without blocking. Every
other `run*` function is built on top of it. Use it when you want a handle you
can observe (await its `Exit`) or interrupt.

```ts
import { Effect, Fiber, Schedule } from "effect"

// A program that logs forever, once every 200ms.
const program = Effect.log("running...").pipe(
  Effect.repeat(Schedule.spaced("200 millis"))
)

// Start it in the background — control returns here immediately.
const fiber = Effect.runFork(program)

// Later, interrupt it. Interruption runs finalizers, so resources are
// released cleanly rather than abandoned.
setTimeout(() => {
  Effect.runFork(Fiber.interrupt(fiber))
}, 500)
```

Unless you specifically need a `Promise` or a synchronous result, `runFork` is a
good default — it gives you the most control. In application entrypoints, prefer
[`runMain`](https://effect.plants.sh/runtime/run-main/), which wraps `runFork` with error reporting and
signal handling.

## runSync

`runSync` executes an effect **synchronously** and returns its value directly.
Use it only when you are certain the effect is purely synchronous and cannot
fail in a way you need to handle. If the effect performs any asynchronous work,
`runSync` throws, because there is no value to return yet.

```ts
import { Effect } from "effect"

const result = Effect.runSync(
  Effect.sync(() => {
    console.log("side effect")
    return 1
  })
)
console.log(result)
// Output:
// side effect
// 1
```

```ts
import { Effect } from "effect"

// Running an async effect synchronously is a usage error — it throws.
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
// throws: (FiberFailure) AsyncFiberException: Fiber cannot be resolved synchronously
```

Treat `runSync` as the exception, not the rule. Synchronous execution is for
edge cases where async simply is not an option.

## The Exit variants

Plain `runSync` and `runPromise` *throw* or *reject* on failure, collapsing the
full failure information. The `*Exit` variants instead return an
[`Exit<A, E>`](https://effect.plants.sh/data-types/) — a value describing the complete outcome, success
or failure. Reach for these when you want to inspect *why* something failed
(including defects and interruption) rather than have it thrown.

```ts
import { Effect, Exit } from "effect"

const exit = Effect.runSyncExit(Effect.fail("boom"))

// Pattern-match on the outcome instead of catching an exception.
if (Exit.isFailure(exit)) {
  console.log("failed with cause:", exit.cause)
} else {
  console.log("succeeded with:", exit.value)
}
// Output: failed with cause: ...Fail("boom")
```

`runPromiseExit` is the async counterpart — it returns a
`Promise<Exit<A, E>>` that always *resolves* (never rejects), so the success and
failure paths are handled uniformly.

```ts
import { Effect } from "effect"

// Always resolves — failure is data in the Exit, not a rejection.
Effect.runPromiseExit(Effect.fail("boom")).then(console.log)
```

## Cheatsheet

| API                     | Given          | Result                |
| ----------------------- | -------------- | --------------------- |
| `Effect.runSync`        | `Effect<A, E>` | `A`                   |
| `Effect.runSyncExit`    | `Effect<A, E>` | `Exit<A, E>`          |
| `Effect.runPromise`     | `Effect<A, E>` | `Promise<A>`          |
| `Effect.runPromiseExit` | `Effect<A, E>` | `Promise<Exit<A, E>>` |
| `Effect.runFork`        | `Effect<A, E>` | `Fiber<A, E>`         |

Each runner also accepts an optional `RunOptions` argument — for example an
`AbortSignal` to cancel execution from the host, or an `uninterruptible` flag.
The `*With` variants (`runForkWith`, `runPromiseWith`, …) take a
`Context` so you can supply services at the moment of execution.

## Sync vs. async is not in the types

Effect intentionally does not track in its type whether an effect runs
synchronously or asynchronously. That information would add significant
complexity and hurt composability for little real safety. The practical
consequence: default to `runPromise` or `runFork` (or `runMain`), and reserve
`runSync` for code you *know* is synchronous.

## Best practice: run at the edge

You should rarely call `run*` more than once or twice in a whole application.
Build your program as composed effects, then run it at the outermost boundary.
For a process entrypoint, don't call these functions directly — use
[`NodeRuntime.runMain` / `BunRuntime.runMain`](https://effect.plants.sh/runtime/run-main/), which add
graceful shutdown and error reporting on top of `runFork`.