1. Effect 是什么?

很多 JavaScript 开发者刚接触 Effect 时,会产生一个疑问:

Effect 是 Node.js 吗?是 TypeScript 的一部分吗?

答案:

都不是。

Effect 是一个独立的 TypeScript/JavaScript 第三方库。

安装:

npm install effect

使用:

import { Effect } from "effect"

它运行在:

Node.js / Bun
        |
        |
        ↓
     Effect

关系:

技术 作用
Node.js JavaScript 运行环境
Bun JavaScript 运行环境
TypeScript 编程语言扩展
Effect 管理复杂程序逻辑的库

2. 为什么需要 Effect?

2.1 普通 JavaScript 的问题

假设实现一个用户登录:

async function login(username, password) {

    const user = await getUser(username)

    const result = await checkPassword(
        user,
        password
    )

    return result
}

看起来很简单。

但是现实中:

login
 |
 |
 +---- 查询数据库
 |
 +---- 验证密码
 |
 +---- 调用第三方服务
 |
 +---- 写入日志

每一步都有可能失败:

  • 数据库连接失败
  • 用户不存在
  • 密码错误
  • 网络超时
  • API 服务异常

于是代码变成:

async function login(username,password){

    try {

        const user =
            await getUser(username)

        const result =
            await checkPassword(
                user,
                password
            )

        return result


    } catch(error){

        console.log(error)

    }
}

当项目变大:

用户登录

 ↓

订单系统

 ↓

支付系统

 ↓

消息系统

 ↓

AI Agent

错误处理、状态管理、依赖管理会越来越复杂。

Effect 就是为了解决这些问题。


3. Effect 的核心思想

普通 JavaScript

函数执行:

调用函数

↓

立即执行

↓

返回结果

例如:

console.log("hello")

立即打印:

hello

Effect

Effect 的思想:

不立即执行,而是先描述一个任务。

例如:

const task =
    Effect.sync(() => {

        console.log("hello")

    })

这里:

没有打印。

只是创建了一个任务:

未来执行:

打印 hello

真正运行:

Effect.runSync(task)

才会输出:

hello

4. Effect 的三个核心泛型

Effect 最常见形式:

Effect<A,E,R>

三个参数:

Effect<
    成功结果,
    错误类型,
    运行环境
>

例如:

Effect<User, LoginError, Database>

表示:

这个任务:

成功:

返回 User

失败:

返回 LoginError

运行需要:

Database

4.1 A:Success Value

A 表示成功结果。

例如:

Effect<User,Error,Database>

成功:

User

4.2 E:Error

E 表示错误。

例如:

Effect<User,UserNotFoundError,Database>

可能失败:

UserNotFoundError

4.3 R:Requirements

R 表示依赖。

例如:

Effect<User,Error,Database>

说明:

运行这个任务需要:

Database

5. Effect 基础 API

学习 Effect,第一阶段需要掌握:

Effect.succeed()
Effect.fail()
Effect.runPromise()

6. Effect.succeed()

作用

创建一个成功任务。

示例:

const task =
    Effect.succeed(100)

表示:

执行任务

↓

成功返回

100

类型:

Effect<number,never,never>

含义:

成功:
number

失败:
不会失败

依赖:
没有

7. Effect.fail()

作用

创建失败任务。

示例:

const error =
    Effect.fail(
        "用户不存在"
    )

表示:

执行任务

↓

失败

↓

返回错误

类型:

Effect<never,string,never>

为什么不用 throw?

传统:

throw new Error(
    "密码错误"
)

问题:

调用者不知道:

这个函数会不会抛异常?

Effect:

Effect.fail(
    "密码错误"
)

错误直接成为类型的一部分:

Effect<User,PasswordError,never>

开发者一眼知道:

可能失败。


8. Effect.runPromise()

前面说:

Effect 只是描述任务。

例如:

const task =
    Effect.succeed(100)

此时:

task

↓

任务描述

还没有执行。

执行:

Effect.runPromise(task)

转换:

Effect

↓

Promise

例如:

Effect.runPromise(task)
.then(value=>{

    console.log(value)

})

输出:

100

9. Effect.gen:组合复杂流程

真实项目不会只有一个任务。

例如 AI Agent:

用户需求

↓

读取文件

↓

调用 GPT

↓

修改代码

↓

执行测试

↓

返回结果

普通 async:

async function agent(){

    const file =
        await readFile()


    const answer =
        await callGPT(file)


    const result =
        await save(answer)


    return result
}

Effect:

const agent =
    Effect.gen(function*(){

        const file =
            yield* readFile()


        const answer =
            yield* callGPT(file)


        const result =
            yield* save(answer)


        return result

    })

其中:

yield*

类似:

await

但是 Effect 额外知道:

  • 哪一步失败
  • 需要什么依赖
  • 如何恢复

10. Error Handling(错误处理)

Effect 最大特点:

Error as Value

错误也是数据。


传统:

throw error

流程:

正常流程

↓

异常跳出

↓

catch

Effect:

错误属于返回类型:

Effect<
    Result,
    AgentError,
    Environment
>

例如:

Effect<
    string,
    FileError | GPTError,
    never
>

表示:

可能失败:

FileError

或者

GPTError

处理:

Effect.catchAll(
    task,
    error => {

        return Effect.succeed(
            "处理失败"
        )

    }
)

11. Layer:依赖注入系统

大型项目最大的问题:

代码耦合。

例如:

function saveUser(){

    mysql.insert()

}

写死:

只能 MySQL

以后换:

PostgreSQL

SQLite

Mock数据库

怎么办?


Effect 使用依赖注入。

思想:

业务代码

↓

只声明需要 Database

↓

运行时提供具体实现

结构:

Agent

↓

Database Service

↓

Layer

↓

SQLite / MySQL

例如:

开发环境:

Mock Database

生产环境:

PostgreSQL

业务代码不用修改。


12. Service:大型项目模块化

AI Agent 通常需要:

GPT Service

File Service

Terminal Service

Memory Service

不要:

import openai
import fs
import child_process

到处调用。

而是:

定义服务:

GPTService

FileService

TerminalService

Agent:

只依赖接口。

结构:

Agent

↓

Service接口

↓

具体实现

↓

GPT / Claude / Local LLM

好处:

以后:

GPT 换 Claude:

不用修改 Agent。


13. Schema:数据验证

外部数据永远不可信。

例如:

用户输入:

{
    "name":"Tom",
    "age":"20"
}

问题:

age 是字符串。

程序需要:

{
    "name":"Tom",
    "age":20
}

Schema 可以:

  • 验证数据
  • 转换数据
  • 保证类型安全

例如:

const User =
    Schema.Struct({

        name: Schema.String,

        age: Schema.Number

    })

要求:

{
    "name":"Tom",
    "age":20
}

14. Effect 学习路线总结

推荐顺序:

第一阶段:任务基础

Effect
 |
 +-- succeed
 |
 +-- fail
 |
 +-- runPromise


第二阶段:任务组合

Effect.gen


第三阶段:可靠性

Error Handling
 |
 +-- Retry
 |
 +-- Timeout
 |
 +-- Logging


第四阶段:大型架构

Service

Layer


第五阶段:数据安全

Schema

15. Effect 在 AI Agent 中的位置

以 Claude Desktop 类应用为例:

Desktop App
    |
    |
    ↓

Agent Core

    |
    |
 -------------------
 |        |         |
GPT    File     Terminal
Service Service Service

    |
    |
 Layer

    |
    |
Database / OS / API

Effect 负责:

功能 Effect模块
任务流程控制 Effect.gen
GPT调用失败处理 Error Handling
文件系统管理 Service
Terminal执行 Service
切换模型 Layer
数据验证 Schema
依赖管理 Layer + Service

总结

如果把普通 JavaScript 比作:

可以写程序的工具

那么 Effect 就像:

一个大型软件工程管理系统。

它让程序拥有:

  • 明确的成功结果
  • 明确的错误类型
  • 可替换的依赖
  • 可组合的任务
  • 可维护的大型架构

对于 AI Agent 开发:

Bun
 ↓
运行环境

SolidJS
 ↓
界面

Effect
 ↓
Agent任务执行引擎

AI SDK
 ↓
GPT / Claude

SQLite
 ↓
数据存储

其中 Effect 是连接所有能力的核心业务逻辑层。