Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'
This commit is contained in:
74
repos/effect/migration/fiber-keep-alive.md
Normal file
74
repos/effect/migration/fiber-keep-alive.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Fiber Keep-Alive: Automatic Process Lifetime Management
|
||||
|
||||
In v3, the core `effect` runtime did **not** keep the Node.js process alive while
|
||||
fibers were suspended on certain asynchronous operations. If a fiber was waiting on
|
||||
something like `Deferred.await` and there was no other work scheduled on the
|
||||
event loop, the process would exit immediately — the fiber's suspension did not
|
||||
register as pending work from Node.js's perspective.
|
||||
|
||||
The only way to prevent this was to use `runMain` from `@effect/platform-node`
|
||||
(or `@effect/platform-bun`), which installed a long-lived `setInterval` timer
|
||||
to hold the process open until the root fiber completed.
|
||||
|
||||
In v4, **the keep-alive mechanism is built into the core runtime**.
|
||||
|
||||
## The Problem in v3
|
||||
|
||||
Consider the following program:
|
||||
|
||||
```ts
|
||||
import { Deferred, Effect } from "effect"
|
||||
|
||||
const program = Effect.gen(function*() {
|
||||
const deferred = yield* Deferred.make<string>()
|
||||
|
||||
yield* Deferred.await(deferred)
|
||||
})
|
||||
|
||||
Effect.runPromise(program)
|
||||
```
|
||||
|
||||
In v3, when the main fiber reached `yield* Deferred.await(deferred)`, it suspended
|
||||
while waiting for the worker fiber to complete the deferred. However, from the
|
||||
JavaScript runtime's perspective, the event loop had no more work to do. Thus,
|
||||
the process would exit.
|
||||
|
||||
The workaround was to use `runMain` from the platform package, which installs
|
||||
a timer that holds the process open until the root fiber completes:
|
||||
|
||||
```ts
|
||||
import { NodeRuntime } from "@effect/platform-node"
|
||||
|
||||
NodeRuntime.runMain(program)
|
||||
```
|
||||
|
||||
## What Changed in v4
|
||||
|
||||
In v4, the Effect fiber runtime automatically manages a reference-counted
|
||||
keep-alive timer.
|
||||
|
||||
This means the following program works in v4 **without** `runMain`:
|
||||
|
||||
```ts
|
||||
import { Deferred, Effect, Fiber } from "effect"
|
||||
|
||||
const program = Effect.gen(function*() {
|
||||
const deferred = yield* Deferred.make<string>()
|
||||
|
||||
// The process stays alive while waiting — no runMain needed
|
||||
yield* Deferred.await(deferred)
|
||||
})
|
||||
|
||||
Effect.runPromise(program)
|
||||
```
|
||||
|
||||
## `runMain` Is Still Recommended
|
||||
|
||||
Even though the core runtime now handles keep-alive, `runMain` from the platform
|
||||
packages is still the recommended way to run Effect programs. It provides:
|
||||
|
||||
- **Signal handling** — listens for `SIGINT` / `SIGTERM` and interrupts the
|
||||
root fiber gracefully.
|
||||
- **Exit code management** — calls `process.exit(code)` when the program fails
|
||||
or receives a signal.
|
||||
- **Error reporting** — reports unhandled errors to the console.
|
||||
Reference in New Issue
Block a user