Effect-TS 提供免费 API:TypeScript 缺失的生产应用标准库

发布: (2026年3月29日 GMT+8 22:09)
3 分钟阅读
原文: Dev.to

Source: Dev.to

概览

Effect 是一个 TypeScript 库,提供 面向生产应用的标准库。它具备以下特性:

  • 类型化错误(明确知道哪些会失败)
  • 依赖注入(无需容器)
  • 结构化并发(纤程、信号量)
  • 带退避的重试策略
  • 架构验证(类似 Zod,但已集成)
  • 流(响应式数据处理)
  • 内置追踪与指标

TypeScript 缺少处理类型化错误、依赖注入、重试、限流或结构化并发的标准方式。Effect 用可组合、类型安全的 API 填补了这些空白。

安装

npm install effect

基本用法

import { Effect, Console } from "effect";

const program = Effect.gen(function* () {
  yield* Console.log("Hello from Effect!");
  const result = yield* Effect.succeed(42);
  yield* Console.log(`Result: ${result}`);
});

Effect.runPromise(program);

类型化错误

import { Effect, Data } from "effect";

class NotFoundError extends Data.TaggedError("NotFoundError") {}

class DatabaseError extends Data.TaggedError("DatabaseError") {}

const findUser = (id: string): Effect.Effect =>
  Effect.gen(function* () {
    const user = yield* queryDatabase(id);
    if (!user) {
      return yield* new NotFoundError({ id });
    }
    return user;
  });

// 处理特定错误
const program = findUser("123").pipe(
  Effect.catchTag("NotFoundError", (e) =>
    Effect.succeed({ name: "Default", id: e.id })
  ),
  Effect.catchTag("DatabaseError", (e) =>
    Effect.fail(new Error(`DB failed: ${e.cause}`))
  ),
);

使用 Context 与 Layers 进行依赖注入

import { Effect, Context, Layer } from "effect";

class Database extends Context.Tag("Database") Effect.Effect }
>() {}

class Logger extends Context.Tag("Logger") Effect.Effect }
>() {}

const findUsers = Effect.gen(function* () {
  const db = yield* Database;
  const logger = yield* Logger;
  yield* logger.log("Fetching users...");
  return yield* db.query("SELECT * FROM users");
});

// 提供实现
const DatabaseLive = Layer.succeed(Database, {
  query: (sql) => Effect.succeed([{ id: 1, name: "Alice" }]),
});

const LoggerLive = Layer.succeed(Logger, {
  log: (msg) => Effect.sync(() => console.log(msg)),
});

const MainLive = Layer.merge(DatabaseLive, LoggerLive);

Effect.runPromise(
  findUsers.pipe(Effect.provide(MainLive))
);

重试、调度与并发

import { Effect, Schedule } from "effect";

const fetchData = Effect.tryPromise(() =>
  fetch("https://api.example.com/data").then((r) => r.json())
);

// 使用指数退避重试(最多 5 次)
const reliable = fetchData.pipe(
  Effect.retry(
    Schedule.exponential("1 second").pipe(
      Schedule.compose(Schedule.recurs(5))
    )
  ),
);

并发运行任务

const tasks = [fetchUser(1), fetchUser(2), fetchUser(3)];

// 并发运行所有任务(最多同时 3 个)
const results = yield* Effect.all(tasks, { concurrency: 3 });

使用信号量限制并发

// 创建一个最多允许 5 个并发许可的信号量
const semaphore = yield* Effect.makeSemaphore(5);

// 为每个任务包装,以在执行前获取许可
const limited = tasks.map((task) => semaphore.withPermits(1)(task));
const results2 = yield* Effect.all(limited);

资源

欢迎探索该库,以构建稳健的、面向生产的 TypeScript 应用。

0 浏览
Back to Blog

相关文章

阅读更多 »

MCP的七宗罪:运营罪

操作罪:懒惰与愤怒 这些罪行属于此类别,因为它们决定了实时 MCP 系统在压力下的行为方式:它是否真实地失败……