Skip to content

沙箱化

错误是编程中不可避免的一部分,它们可能来自各种来源,如失败、缺陷、纤程中断或这些因素的组合。本指南解释了如何使用 Effect.sandbox 函数来隔离和理解基于 Effect 的代码中错误的原因。

Effect.sandbox 函数允许您封装 Effect 中错误的所有潜在原因。它暴露了 Effect 的完整原因,无论是由于失败、缺陷、纤程中断还是这些因素的组合。

简单来说,它接受一个 Effect Effect<A, E, R> 并将其转换为 Effect Effect<A, Cause<E>, R>,其中错误通道现在包含错误的详细原因。

Syntax

Effect<A, E, R> -> Effect<A, Cause<E>, R>

通过使用 Effect.sandbox 函数,您可以访问异常 Effect 的底层原因。这些原因表示为 Cause<E> 类型,并在 Effect 数据类型的错误通道中可用。

一旦您暴露了原因,就可以利用标准的错误处理操作符,如 Effect.catchAllEffect.catchTags 来更有效地处理错误。这些操作符允许您响应特定的错误条件。

如果需要,我们可以使用 Effect.unsandbox 撤销沙箱化操作。

示例(处理不同的错误原因)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Console
Console
} from "effect"
// ┌─── Effect<string, Error, never>
// ▼
const
const task: Effect.Effect<string, Error, never>
task
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <Error>(error: Error) => Effect.Effect<never, Error, never>

Creates an Effect that represents a recoverable error.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

Example (Creating a Failed Effect)

import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@seesucceed to create an effect that represents a successful value.

@since2.0.0

fail
(new
var Error: ErrorConstructor
new (message?: string) => Error
Error
("Oh uh!")).
Pipeable.pipe<Effect.Effect<never, Error, never>, Effect.Effect<string, Error, never>>(this: Effect.Effect<never, Error, never>, ab: (_: Effect.Effect<never, Error, never>) => Effect.Effect<string, Error, never>): Effect.Effect<string, Error, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

Example (Replacing a Value)

import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("primary result")
)
// ┌─── Effect<string, Cause<Error>, never>
// ▼
const
const sandboxed: Effect.Effect<string, Cause<Error>, never>
sandboxed
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sandbox: <string, Error, never>(self: Effect.Effect<string, Error, never>) => Effect.Effect<string, Cause<Error>, never>

Transforms an effect to expose detailed error causes.

Details

This function enhances an effect by providing detailed information about any error, defect, or interruption that may occur during its execution. It modifies the error channel of the effect so that it includes a full cause of the failure, wrapped in a Cause<E> type.

After applying this function, you can use operators like

catchAll

and

catchTags

to handle specific types of errors.

If you no longer need the detailed cause information, you can revert the changes using

unsandbox

to return to the original error-handling behavior.

Example

import { Effect, Console } from "effect"
// ┌─── Effect<string, Error, never>
// ▼
const task = Effect.fail(new Error("Oh uh!")).pipe(
Effect.as("primary result")
)
// ┌─── Effect<string, Cause<Error>, never>
// ▼
const sandboxed = Effect.sandbox(task)
const program = Effect.catchTags(sandboxed, {
Die: (cause) =>
Console.log(`Caught a defect: ${cause.defect}`).pipe(
Effect.as("fallback result on defect")
),
Interrupt: (cause) =>
Console.log(`Caught a defect: ${cause.fiberId}`).pipe(
Effect.as("fallback result on fiber interruption")
),
Fail: (cause) =>
Console.log(`Caught a defect: ${cause.error}`).pipe(
Effect.as("fallback result on failure")
)
})
// Restore the original error handling with unsandbox
const main = Effect.unsandbox(program)
Effect.runPromise(main).then(console.log)
// Output:
// Caught a defect: Oh uh!
// fallback result on failure

@seeunsandbox to restore the original error handling.

@since2.0.0

sandbox
(
const task: Effect.Effect<string, Error, never>
task
)
const
const program: Effect.Effect<string, Empty | Sequential<Error> | Parallel<Error>, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchTags: <never, Cause<Error>, string, {
Die: (cause: Die) => Effect.Effect<string, never, never>;
Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>;
Fail: (cause: Fail<Error>) => Effect.Effect<string, never, never>;
}>(self: Effect.Effect<string, Cause<Error>, never>, cases: {
Die: (cause: Die) => Effect.Effect<string, never, never>;
Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>;
Fail: (cause: Fail<Error>) => Effect.Effect<string, never, never>;
}) => Effect.Effect<...> (+1 overload)

Handles multiple errors in a single block of code using their _tag field.

When to Use

catchTags is a convenient way to handle multiple error types at once. Instead of using

catchTag

multiple times, you can pass an object where each key is an error type's _tag, and the value is the handler for that specific error. This allows you to catch and recover from multiple error types in a single call.

The error type must have a readonly _tag field to use catchTag. This field is used to identify and match errors.

Example (Handling Multiple Tagged Error Types at Once)

import { Effect, Random } from "effect"
class HttpError {
readonly _tag = "HttpError"
}
class ValidationError {
readonly _tag = "ValidationError"
}
// ┌─── Effect<string, HttpError | ValidationError, never>
// ▼
const program = Effect.gen(function* () {
const n1 = yield* Random.next
const n2 = yield* Random.next
if (n1 < 0.5) {
yield* Effect.fail(new HttpError())
}
if (n2 < 0.5) {
yield* Effect.fail(new ValidationError())
}
return "some result"
})
// ┌─── Effect<string, never, never>
// ▼
const recovered = program.pipe(
Effect.catchTags({
HttpError: (_HttpError) =>
Effect.succeed(`Recovering from HttpError`),
ValidationError: (_ValidationError) =>
Effect.succeed(`Recovering from ValidationError`)
})
)

@since2.0.0

catchTags
(
const sandboxed: Effect.Effect<string, Cause<Error>, never>
sandboxed
, {
type Die: (cause: Die) => Effect.Effect<string, never, never>
Die
: (
cause: Die
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Die
cause
.
Die.defect: unknown
defect
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<string, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

Example (Replacing a Value)

import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on defect")
),
type Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>
Interrupt
: (
cause: Interrupt
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Interrupt
cause
.
Interrupt.fiberId: FiberId
fiberId
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<string, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

Example (Replacing a Value)

import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on fiber interruption")
),
type Fail: (cause: Fail<Error>) => Effect.Effect<string, never, never>
Fail
: (
cause: Fail<Error>
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Fail<Error>
cause
.
Fail<Error>.error: Error
error
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<string, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

Example (Replacing a Value)

import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on failure")
)
})
// 使用 unsandbox 恢复原始错误处理
const
const main: Effect.Effect<string, Error, never>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const unsandbox: <string, Error, never>(self: Effect.Effect<string, Cause<Error>, never>) => Effect.Effect<string, Error, never>

The unsandbox function is used to revert an effect that has been sandboxed by

sandbox

. When you apply unsandbox, the effect's error channel is restored to its original state, without the detailed Cause<E> information. This means that any underlying causes of errors, defects, or fiber interruptions are no longer exposed in the error channel.

This function is useful when you want to remove the detailed error tracking provided by sandbox and return to the standard error handling for your effect. Once unsandboxed, the effect behaves as if sandbox was never applied.

@seesandbox to expose the full cause of failures, defects, or interruptions.

@since2.0.0

unsandbox
(
const program: Effect.Effect<string, Empty | Sequential<Error> | Parallel<Error>, never>
program
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <string, Error>(effect: Effect.Effect<string, Error, never>, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<string>

Executes an effect and returns the result as a Promise.

Details

This function runs an effect and converts its result into a Promise. If the effect succeeds, the Promise will resolve with the successful result. If the effect fails, the Promise will reject with an error, which includes the failure details of the effect.

The optional options parameter allows you to pass an AbortSignal for cancellation, enabling more fine-grained control over asynchronous tasks.

When to Use

Use this function when you need to execute an effect and work with its result in a promise-based system, such as when integrating with third-party libraries that expect Promise results.

Example (Running a Successful Effect as a Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

Example (Handling a Failing Effect as a Rejected Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@since2.0.0

runPromise
(
const main: Effect.Effect<string, Error, never>
main
).
Promise<string>.then<void, never>(onfulfilled?: ((value: string) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
globalThis.Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
)
/*
Output:
Caught a defect: Oh uh!
fallback result on failure
*/