You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Understand what Effect gives you that Promise doesn't: type-safe errors, dependency injection, and composability.
tags
getting-started
comparison
promise
motivation
type-safety
rule
description
Understand why Effect is better than raw Promises.
related
getting-started-hello-world
effects-are-lazy
execute-with-runpromise
author
Paul Philp
Why Effect? Comparing Effect to Promise
Guideline
Effect solves three problems that Promises don't:
Errors are typed - You know exactly what can go wrong
Dependencies are tracked - You know what services are needed
Effects are lazy - Nothing runs until you say so
The Problem with Promises
// With Promises, errors are invisible in the type systemasyncfunctionfetchUser(id: string): Promise<User>{constresponse=awaitfetch(`/api/users/${id}`);if(!response.ok)thrownewError("Failed to fetch");// What errors can happen?returnresponse.json();}// The type says Promise<User>, but it might throw anything!
The Effect Solution
import{Effect}from"effect";classFetchError{readonly_tag="FetchError";constructor(readonlymessage: string){}}classParseError{readonly_tag="ParseError";constructor(readonlymessage: string){}}// The type tells you EXACTLY what can go wrongconstfetchUser=(id: string): Effect.Effect<User,FetchError|ParseError>=>Effect.gen(function*(){constresponse=yield*Effect.tryPromise({try: ()=>fetch(`/api/users/${id}`),catch: ()=>newFetchError("Network request failed"),});if(!response.ok){returnyield*Effect.fail(newFetchError(`HTTP ${response.status}`));}constjson=yield*Effect.tryPromise({try: ()=>response.json(),catch: ()=>newParseError("Invalid JSON"),});returnjsonasUser;});
Side-by-Side Comparison
Feature
Promise
Effect
Error types
any (unknown)
Explicit in type signature
Execution
Immediate (eager)
Lazy (runs when you say)
Dependencies
Global/implicit
Explicit in type signature
Composition
.then() chains
Powerful combinators
Cancellation
AbortController (manual)
Built-in fiber interruption
Retry/Timeout
Manual implementation
One-liner with Schedule
Key Insight
With Promises:
// You have to read the implementation to know what might fail
async function doSomething(): Promise<Result> { ... }
With Effect:
// The type tells you everything
function doSomething(): Effect<Result, NetworkError | ParseError, Database> { ... }
// ^success ^errors ^dependencies
When to Use Effect
✅ Complex business logic with multiple error types
✅ Applications needing dependency injection
✅ Code that needs to be testable
✅ Operations needing retry, timeout, or concurrency control