Effect vs Promise
In this guide, we will explore the differences between Promise and Effect, two approaches to handling asynchronous operations in TypeScript. We’ll discuss their type safety, creation, chaining, and concurrency, providing examples to help you understand their usage.
Comparing Effects and Promises: Key Distinctions
- Evaluation Strategy: Promises are eagerly evaluated, whereas effects are lazily evaluated.
- Execution Mode: Promises are one-shot, executing once, while effects are multi-shot, repeatable.
- Interruption Handling and Automatic Propagation: Promises lack built-in interruption handling, posing challenges in managing interruptions, and don’t automatically propagate interruptions, requiring manual abort controller management. In contrast, effects come with interruption handling capabilities and automatically compose interruption, simplifying management locally on smaller computations without the need for high-level orchestration.
- Structured Concurrency: Effects offer structured concurrency built-in, which is challenging to achieve with Promises.
- Error Reporting (Type Safety): Promises don’t inherently provide detailed error reporting at the type level, whereas effects do, offering type-safe insight into error cases.
- Runtime Behavior: The Effect runtime aims to remain synchronous as long as possible, transitioning into asynchronous mode only when necessary due to computation requirements or main thread starvation.
Type safety
Let’s start by comparing the types of Promise and Effect. The type parameter A represents the resolved value of the operation:
Promise<A>Effect<A, Error, Context>Here’s what sets Effect apart:
- It allows you to track the types of errors statically through the type parameter
Error. For more information about error management inEffect, see Expected Errors. - It allows you to track the types of required dependencies statically through the type parameter
Context. For more information about context management inEffect, see Managing Services.
Creating
Success
Let’s compare creating a successful operation using Promise and Effect:
const const success: Promise<number>
success = var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(2)import { import Effect
Effect } from "effect"
const const success: Effect.Effect<number, never, never>
success = import Effect
Effect.const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed(2)Failure
Now, let’s see how to handle failures with Promise and Effect:
const const failure: Promise<never>
failure = var Promise: PromiseConstructor
Promise.PromiseConstructor.reject<never>(reason?: any): Promise<never>
reject("Uh oh!")import { import Effect
Effect } from "effect"
const const failure: Effect.Effect<never, string, never>
failure = import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")Constructor
Creating operations with custom logic:
const const task: Promise<number>
task = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var Math: Math
Math.Math.random(): number
random() > 0.5 ? resolve: (value: number | PromiseLike<number>) => void
resolve(2) : reject: (reason?: any) => void
reject("Uh oh!") }, 300)})import { import Effect
Effect } from "effect"
const const task: Effect.Effect<number, string, never>
task = import Effect
Effect.const gen: <Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, string, never> (+1 overload)
gen(function* () { yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("300 millis") return var Math: Math
Math.Math.random(): number
random() > 0.5 ? 2 : yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")})Thenable
Mapping the result of an operation:
map
const const mapped: Promise<number>
mapped = var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<string>(value: string): Promise<string> (+2 overloads)
resolve("Hello").Promise<string>.then<number, never>(onfulfilled?: ((value: string) => number | PromiseLike<number>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<number>
then((s: string
s) => s: string
s.String.length: number
length)import { import Effect
Effect } from "effect"
const const mapped: Effect.Effect<number, never, never>
mapped = import Effect
Effect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>
succeed("Hello").Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<string, never, never>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<number, never, never> (+21 overloads)
pipe( import Effect
Effect.const map: <string, number>(f: (a: string) => number) => <E, R>(self: Effect.Effect<string, E, R>) => Effect.Effect<number, E, R> (+1 overload)
map((s: string
s) => s: string
s.String.length: number
length), // or Effect.andThen((s) => s.length))flatMap
Chaining multiple operations:
const const flatMapped: Promise<number>
flatMapped = var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<string>(value: string): Promise<string> (+2 overloads)
resolve("Hello").Promise<string>.then<number, never>(onfulfilled?: ((value: string) => number | PromiseLike<number>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<number>
then((s: string
s) => var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(s: string
s.String.length: number
length),)import { import Effect
Effect } from "effect"
const const flatMapped: Effect.Effect<number, never, never>
flatMapped = import Effect
Effect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>
succeed("Hello").Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<string, never, never>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<number, never, never> (+21 overloads)
pipe( import Effect
Effect.const flatMap: <string, number, never, never>(f: (a: string) => Effect.Effect<number, never, never>) => <E, R>(self: Effect.Effect<string, E, R>) => Effect.Effect<number, E, R> (+1 overload)
flatMap((s: string
s) => import Effect
Effect.const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed(s: string
s.String.length: number
length)), // or Effect.andThen((s) => Effect.succeed(s.length)))Comparing Effect.gen with async/await
If you are familiar with async/await, you may notice that the flow of writing code is similar.
Let’s compare the two approaches:
const const increment: (x: number) => number
increment = (x: number
x: number) => x: number
x + 1
const const divide: (a: number, b: number) => Promise<number>
divide = (a: number
a: number, b: number
b: number): interface Promise<T>
Promise<number> => b: number
b === 0 ? var Promise: PromiseConstructor
Promise.PromiseConstructor.reject<number>(reason?: any): Promise<number>
reject(new var Error: ErrorConstructornew (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error("Cannot divide by zero")) : var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(a: number
a / b: number
b)
const const task1: Promise<number>
task1 = var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(10)
const const task2: Promise<number>
task2 = var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(2)
const const program: () => Promise<string>
program = async function () { const const a: number
a = await const task1: Promise<number>
task1 const const b: number
b = await const task2: Promise<number>
task2 const const n1: number
n1 = await const divide: (a: number, b: number) => Promise<number>
divide(const a: number
a, const b: number
b) const const n2: number
n2 = const increment: (x: number) => number
increment(const n1: number
n1) return `Result is: ${const n2: number
n2}`}
const program: () => Promise<string>
program().Promise<string>.then<void, never>(onfulfilled?: ((value: string) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log) // Output: "Result is: 6"import { import Effect
Effect } from "effect"
const const increment: (x: number) => number
increment = (x: number
x: number) => x: number
x + 1
const const divide: (a: number, b: number) => Effect.Effect<number, Error>
divide = (a: number
a: number, b: number
b: number): import Effect
Effect.interface Effect<out A, out E = never, out R = never>
Effect<number, interface Error
Error> => b: number
b === 0 ? import Effect
Effect.const fail: <Error>(error: Error) => Effect.Effect<never, Error, never>
fail(new var Error: ErrorConstructornew (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error("Cannot divide by zero")) : import Effect
Effect.const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed(a: number
a / b: number
b)
const const task1: Effect.Effect<number, never, never>
task1 = import Effect
Effect.const promise: <number>(evaluate: (signal: AbortSignal) => PromiseLike<number>) => Effect.Effect<number, never, never>
promise(() => var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(10))
const const task2: Effect.Effect<number, never, never>
task2 = import Effect
Effect.const promise: <number>(evaluate: (signal: AbortSignal) => PromiseLike<number>) => Effect.Effect<number, never, never>
promise(() => var Promise: PromiseConstructor
Promise.PromiseConstructor.resolve<number>(value: number): Promise<number> (+2 overloads)
resolve(2))
const const program: Effect.Effect<string, Error, never>
program = import Effect
Effect.const gen: <Effect.Effect<number, Error, never>, string>(f: () => Generator<Effect.Effect<number, Error, never>, string, never>) => Effect.Effect<string, Error, never> (+1 overload)
gen(function* () { const const a: number
a = yield* const task1: Effect.Effect<number, never, never>
task1 const const b: number
b = yield* const task2: Effect.Effect<number, never, never>
task2 const const n1: number
n1 = yield* const divide: (a: number, b: number) => Effect.Effect<number, Error>
divide(const a: number
a, const b: number
b) const const n2: number
n2 = const increment: (x: number) => number
increment(const n1: number
n1) return `Result is: ${const n2: number
n2}`})
import Effect
Effect.const runPromise: <string, Error>(effect: Effect.Effect<string, Error, never>, options?: Effect.RunOptions | undefined) => Promise<string>
runPromise(const program: Effect.Effect<string, Error, never>
program).Promise<string>.then<void, never>(onfulfilled?: ((value: string) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log)// Output: "Result is: 6"It’s important to note that although the code appears similar, the two programs are not identical. The purpose of comparing them side by side is just to highlight the resemblance in how they are written.
Concurrency
Promise.all()
const const task1: Promise<number>
task1 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task1 done") resolve: (value: number | PromiseLike<number>) => void
resolve(1) }, 100)})
const const task2: Promise<number>
task2 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task2 done") reject: (reason?: any) => void
reject("Uh oh!") }, 200)})
const const task3: Promise<number>
task3 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task3 done") resolve: (value: number | PromiseLike<number>) => void
resolve(3) }, 300)})
const const program: Promise<[number, number, number]>
program = var Promise: PromiseConstructor
Promise.PromiseConstructor.all<[Promise<number>, Promise<number>, Promise<number>]>(values: [Promise<number>, Promise<number>, Promise<number>]): Promise<[number, number, number]> (+1 overload)
all([const task1: Promise<number>
task1, const task2: Promise<number>
task2, const task3: Promise<number>
task3])
const program: Promise<[number, number, number]>
program.Promise<[number, number, number]>.then<void, void>(onfulfilled?: ((value: [number, number, number]) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)/*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 doneUh oh!task3 done*/import { import Effect
Effect } from "effect"
const const task1: Effect.Effect<number, never, never>
task1 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("100 millis") var console: Console
console.Console.log(...data: any[]): void
log("task1 done") return 1})
const const task2: Effect.Effect<never, string, never>
task2 = import Effect
Effect.const gen: <Effect.Effect<void, never, never> | Effect.Effect<never, string, never>, never>(f: () => Generator<Effect.Effect<void, never, never> | Effect.Effect<never, string, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("200 millis") var console: Console
console.Console.log(...data: any[]): void
log("task2 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")})
const const task3: Effect.Effect<number, never, never>
task3 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("300 millis") var console: Console
console.Console.log(...data: any[]): void
log("task3 done") return 3})
const const program: Effect.Effect<[number, never, number], string, never>
program = import Effect
Effect.const all: <readonly [Effect.Effect<number, never, never>, Effect.Effect<never, string, never>, Effect.Effect<number, never, never>], { concurrency: "unbounded";}>(arg: readonly [Effect.Effect<number, never, never>, Effect.Effect<never, string, never>, Effect.Effect<number, never, never>], options?: { concurrency: "unbounded";} | undefined) => Effect.Effect<[number, never, number], string, never>
all([const task1: Effect.Effect<number, never, never>
task1, const task2: Effect.Effect<never, string, never>
task2, const task3: Effect.Effect<number, never, never>
task3], { concurrency: "unbounded"
concurrency: "unbounded",})
import Effect
Effect.const runPromise: <[number, never, number], string>(effect: Effect.Effect<[number, never, number], string, never>, options?: Effect.RunOptions | undefined) => Promise<[number, never, number]>
runPromise(const program: Effect.Effect<[number, never, number], string, never>
program).Promise<[number, never, number]>.then<void, void>(onfulfilled?: ((value: [number, never, number]) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)/*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 done(FiberFailure) Error: Uh oh!*/Promise.allSettled()
const task1 = new Promise<number>((resolve, reject) => { console.log("Executing task1...") setTimeout(() => { console.log("task1 done") resolve(1) }, 100)})
const task2 = new Promise<number>((resolve, reject) => { console.log("Executing task2...") setTimeout(() => { console.log("task2 done") reject("Uh oh!") }, 200)})
const task3 = new Promise<number>((resolve, reject) => { console.log("Executing task3...") setTimeout(() => { console.log("task3 done") resolve(3) }, 300)})
const program = Promise.allSettled([task1, task2, task3])
program.then(console.log, console.error)/*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 donetask3 done[ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 'Uh oh!' }, { status: 'fulfilled', value: 3 }]*/import { import Effect
Effect } from "effect"
const const task1: Effect.Effect<number, never, never>
task1 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("100 millis") var console: Console
console.Console.log(...data: any[]): void
log("task1 done") return 1})
const const task2: Effect.Effect<never, string, never>
task2 = import Effect
Effect.const gen: <Effect.Effect<void, never, never> | Effect.Effect<never, string, never>, never>(f: () => Generator<Effect.Effect<void, never, never> | Effect.Effect<never, string, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("200 millis") var console: Console
console.Console.log(...data: any[]): void
log("task2 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")})
const const task3: Effect.Effect<number, never, never>
task3 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("300 millis") var console: Console
console.Console.log(...data: any[]): void
log("task3 done") return 3})
const const program: Effect.Effect<unknown[], unknown, unknown>
program = import Effect
Effect.const forEach: <unknown, unknown, unknown, (Effect.Effect<number, never, never> | Effect.Effect<never, string, never>)[], false>(self: (Effect.Effect<number, never, never> | Effect.Effect<never, string, never>)[], f: (a: Effect.Effect<number, never, never> | Effect.Effect<never, string, never>, i: number) => Effect.Effect<unknown, unknown, unknown>, options?: { readonly concurrency?: Concurrency | undefined; readonly discard?: false | undefined;} | undefined) => Effect.Effect<unknown[], unknown, unknown> (+1 overload)
forEach( [const task1: Effect.Effect<number, never, never>
task1, const task2: Effect.Effect<never, string, never>
task2, const task3: Effect.Effect<number, never, never>
task3], (task: Effect.Effect<number, never, never> | Effect.Effect<never, string, never>
task) => import Effect
Effect.either(task: Effect.Effect<number, never, never> | Effect.Effect<never, string, never>
task), // or Effect.exitError ts(2339) ― { concurrency?: Concurrency | undefined
concurrency: "unbounded", },)
import Effect
Effect.const runPromise: <unknown[], unknown>(effect: Effect.Effect<unknown[], unknown, never>, options?: Effect.RunOptions | undefined) => Promise<unknown[]>
runPromise(program).Promise<unknown[]>.then<void, void>(onfulfilled?: ((value: unknown[]) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)Error ts(2345) ― /*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 donetask3 done[ { _id: "Either", _tag: "Right", right: 1 }, { _id: "Either", _tag: "Left", left: "Uh oh!" }, { _id: "Either", _tag: "Right", right: 3 }]*/Promise.any()
const task1 = new Promise<number>((resolve, reject) => { console.log("Executing task1...") setTimeout(() => { console.log("task1 done") reject("Something went wrong!") }, 100)})
const task2 = new Promise<number>((resolve, reject) => { console.log("Executing task2...") setTimeout(() => { console.log("task2 done") resolve(2) }, 200)})
const task3 = new Promise<number>((resolve, reject) => { console.log("Executing task3...") setTimeout(() => { console.log("task3 done") reject("Uh oh!") }, 300)})
const program = Promise.any([task1, task2, task3])
program.then(console.log, console.error)/*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 done2task3 done*/import { import Effect
Effect } from "effect"
const const task1: Effect.Effect<never, string, never>
task1 = import Effect
Effect.const gen: <Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never>(f: () => Generator<Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("100 millis") var console: Console
console.Console.log(...data: any[]): void
log("task1 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Something went wrong!")})
const const task2: Effect.Effect<number, never, never>
task2 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("200 millis") var console: Console
console.Console.log(...data: any[]): void
log("task2 done") return 2})
const const task3: Effect.Effect<never, string, never>
task3 = import Effect
Effect.const gen: <Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never>(f: () => Generator<Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("300 millis") var console: Console
console.Console.log(...data: any[]): void
log("task3 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")})
const const program: Effect.Effect<number, string, never>
program = import Effect
Effect.const raceAll: <Effect.Effect<never, string, never> | Effect.Effect<number, never, never>>(all: Iterable<Effect.Effect<never, string, never> | Effect.Effect<number, never, never>>, options?: { readonly onWinner?: (options: { readonly fiber: Fiber<any, any>; readonly index: number; readonly parentFiber: Fiber<any, any>; }) => void;}) => Effect.Effect<number, string, never>
raceAll([const task1: Effect.Effect<never, string, never>
task1, const task2: Effect.Effect<number, never, never>
task2, const task3: Effect.Effect<never, string, never>
task3])
import Effect
Effect.const runPromise: <number, string>(effect: Effect.Effect<number, string, never>, options?: Effect.RunOptions | undefined) => Promise<number>
runPromise(const program: Effect.Effect<number, string, never>
program).Promise<number>.then<void, void>(onfulfilled?: ((value: number) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)/*Output:Executing task1...Executing task2...Executing task3...task1 donetask2 done2*/Promise.race()
const const task1: Promise<number>
task1 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task1 done") reject: (reason?: any) => void
reject("Something went wrong!") }, 100)})
const const task2: Promise<number>
task2 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task2 done") reject: (reason?: any) => void
reject("Uh oh!") }, 200)})
const const task3: Promise<number>
task3 = new var Promise: PromiseConstructornew <number>(executor: (resolve: (value: number | PromiseLike<number>) => void, reject: (reason?: any) => void) => void) => Promise<number>
Promise<number>((resolve: (value: number | PromiseLike<number>) => void
resolve, reject: (reason?: any) => void
reject) => { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number
setTimeout(() => { var console: Console
console.Console.log(...data: any[]): void
log("task3 done") resolve: (value: number | PromiseLike<number>) => void
resolve(3) }, 300)})
const const program: Promise<number>
program = var Promise: PromiseConstructor
Promise.PromiseConstructor.race<[Promise<number>, Promise<number>, Promise<number>]>(values: [Promise<number>, Promise<number>, Promise<number>]): Promise<number> (+1 overload)
race([const task1: Promise<number>
task1, const task2: Promise<number>
task2, const task3: Promise<number>
task3])
const program: Promise<number>
program.Promise<number>.then<void, void>(onfulfilled?: ((value: number) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)/*Output:Executing task1...Executing task2...Executing task3...task1 doneSomething went wrong!task2 donetask3 done*/import { import Effect
Effect } from "effect"
const const task1: Effect.Effect<never, string, never>
task1 = import Effect
Effect.const gen: <Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never>(f: () => Generator<Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task1...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("100 millis") var console: Console
console.Console.log(...data: any[]): void
log("task1 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Something went wrong!")})
const const task2: Effect.Effect<never, string, never>
task2 = import Effect
Effect.const gen: <Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never>(f: () => Generator<Effect.Effect<never, string, never> | Effect.Effect<void, never, never>, never, never>) => Effect.Effect<never, string, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task2...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("200 millis") var console: Console
console.Console.log(...data: any[]): void
log("task2 done") return yield* import Effect
Effect.const fail: <string>(error: string) => Effect.Effect<never, string, never>
fail("Uh oh!")})
const const task3: Effect.Effect<number, never, never>
task3 = import Effect
Effect.const gen: <Effect.Effect<void, never, never>, number>(f: () => Generator<Effect.Effect<void, never, never>, number, never>) => Effect.Effect<number, never, never> (+1 overload)
gen(function* () { var console: Console
console.Console.log(...data: any[]): void
log("Executing task3...") yield* import Effect
Effect.const sleep: (duration: Input) => Effect.Effect<void>
sleep("300 millis") var console: Console
console.Console.log(...data: any[]): void
log("task3 done") return 3})
const const program: Effect.Effect<any, any, any>
program = import Effect
Effect.const raceAll: <Effect.Effect<any, any, any>>(all: Iterable<Effect.Effect<any, any, any>>, options?: { readonly onWinner?: (options: { readonly fiber: Fiber<any, any>; readonly index: number; readonly parentFiber: Fiber<any, any>; }) => void;}) => Effect.Effect<any, any, any>
raceAll([const task1: Effect.Effect<never, string, never>
task1, const task2: Effect.Effect<never, string, never>
task2, const task3: Effect.Effect<number, never, never>
task3].Array<Effect<never, string, never> | Effect<number, never, never>>.map<unknown>(callbackfn: (value: Effect.Effect<never, string, never> | Effect.Effect<number, never, never>, index: number, array: (Effect.Effect<never, string, never> | Effect.Effect<number, never, never>)[]) => unknown, thisArg?: any): unknown[]
map(import Effect
Effect.either)) // or Effect.exitError ts(2345) ― Error ts(2339) ―
import Effect
Effect.const runPromise: <any, any>(effect: Effect.Effect<any, any, never>, options?: Effect.RunOptions | undefined) => Promise<any>
runPromise(program).Promise<any>.then<void, void>(onfulfilled?: ((value: any) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
then(var console: Console
console.Console.log(...data: any[]): void
log, var console: Console
console.Console.error(...data: any[]): void
error)Error ts(2345) ― /*Output:Executing task1...Executing task2...Executing task3...task1 done{ _id: "Either", _tag: "Left", left: "Something went wrong!"}*/FAQ
Question. What is the equivalent of starting a promise without immediately waiting for it in Effects?
const task = (delay: number, name: string) => new Promise((resolve) => setTimeout(() => { console.log(`${name} done`) return resolve(name) }, delay), )
export async function program() { const r0 = task(2_000, "long running task") const r1 = await task(200, "task 2") const r2 = await task(100, "task 3") return { r1, r2, r0: await r0, }}
program().then(console.log)/*Output:task 2 donetask 3 donelong running task done{ r1: 'task 2', r2: 'task 3', r0: 'long running promise' }*/Answer: You can achieve this by utilizing Effect.fork and Fiber.join.
import { Effect, Fiber } from "effect"
const task = (delay: number, name: string) => Effect.gen(function* () { yield* Effect.sleep(delay) console.log(`${name} done`) return name })
const program = Effect.gen(function* () { const r0 = yield* Effect.fork(task(2_000, "long running task")) const r1 = yield* task(200, "task 2") const r2 = yield* task(100, "task 3") return { r1, r2, r0: yield* Fiber.join(r0), }})
Effect.runPromise(program).then(console.log)/*Output:task 2 donetask 3 donelong running task done{ r1: 'task 2', r2: 'task 3', r0: 'long running promise' }*/