Effect-TS入门模式Skill effect-patterns-getting-started

这个技能提供6个精心挑选的Effect-TS入门模式,涵盖错误重试、值变换、并行处理等核心功能,帮助开发者快速上手Effect-TS应用,优化代码质量。关键词:Effect-TS, 入门, 模式, 函数式编程, TypeScript, 并发控制, 最佳实践。

前端开发 0 次安装 0 次浏览 更新于 3/8/2026

名称: effect-patterns-getting-started 描述: Effect-TS入门模式。在Effect-TS应用中入门时使用此技能。

Effect-TS模式:入门

此技能提供了6个精心挑选的Effect-TS入门模式。 在以下任务相关时使用此技能:

  • 入门
  • Effect-TS应用中的最佳实践
  • 真实世界模式和解决方案

🟢 入门模式

使用Effect.retry重试失败的操作

规则: 使用Effect.retry重试失败的操作。

好示例:

import { Effect, Schedule, pipe } from "effect";

class ApiError {
  readonly _tag = "ApiError";
  constructor(readonly status: number) {}
}

const fetchUserData = (userId: string) =>
  Effect.tryPromise({
    try: async () => {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) throw new ApiError(response.status);
      return response.json();
    },
    catch: (error) => error as ApiError,
  });

// 重试最多3次,每次间隔500毫秒
const fetchWithRetry = (userId: string) =>
  pipe(
    fetchUserData(userId),
    Effect.retry(
      Schedule.recurs(3).pipe(Schedule.addDelay(() => "500 millis"))
    ),
    Effect.catchAll((error) =>
      Effect.succeed({ error: `重试后失败: ${error._tag}` })
    )
  );

原理:

使用Effect.retry来自动重试失败的Effect。结合Schedule来控制重试次数和重试间隔。

网络请求会失败。数据库会超时。服务会暂时宕机。 与其立即失败,通常你希望重试几次。 Effect让这变成一行代码。


你好世界:你的第一个Effect程序

规则: 使用Effect.succeed创建你的第一个Effect程序。

好示例:

import { Effect } from "effect";

// 步骤1:创建一个成功带有值的Effect
const helloWorld = Effect.succeed("你好,Effect!");

// 步骤2:运行Effect并获取结果
const result = Effect.runSync(helloWorld);

console.log(result); // "你好,Effect!"

原理:

使用Effect.succeed包装一个值来创建你的第一个Effect,然后使用Effect.runSync运行它以查看结果。

每个旅程都从"你好世界"开始。在Effect中,你通过描述你想要发生的事情来创建计算,然后运行它们。这种分离正是Effect强大的地方。


使用Effect.map变换值

规则: 使用map变换Effect值。

好示例:

import { Effect } from "effect";

// 从一个成功带有数字的Effect开始
const getNumber = Effect.succeed(5);

// 变换它:乘以2
const doubled = Effect.map(getNumber, (n) => n * 2);

// 再次变换:转换为字符串
const asString = Effect.map(doubled, (n) => `结果是 ${n}`);

// 运行以查看结果
const result = Effect.runSync(asString);
console.log(result); // "结果是 10"

原理:

使用Effect.map来变换Effect内部的成功值。变换函数接收值并返回一个新值。

就像Array.map变换数组元素一样,Effect.map变换Effect的成功值。这让你可以构建变换管道,而无需在最后之前运行任何内容。


使用Effect.fail和catchAll处理第一个错误

规则: 使用Effect.fail和catchAll处理错误。

好示例:

import { Effect, pipe } from "effect";

class UserNotFound {
  readonly _tag = "UserNotFound";
  constructor(readonly id: string) {}
}

const findUser = (id: string) =>
  id === "123"
    ? Effect.succeed({ id, name: "Alice" })
    : Effect.fail(new UserNotFound(id));

const program = pipe(
  findUser("456"),
  Effect.catchTag("UserNotFound", (e) =>
    Effect.succeed({ id: e.id, name: "Guest" })
  ),
  Effect.map((user) => `你好,${user.name}!`)
);

const result = Effect.runSync(program);
console.log(result); // "你好,Guest!"

原理:

使用Effect.fail创建一个失败带有错误的Effect,并使用Effect.catchAll从该失败中恢复。

真实程序会失败。Effect在类型系统中使失败显式化,这样你就不会忘记处理它们。与try/catch不同,Effect错误在类型中被追踪。


使用Effect.all并行运行多个Effect

规则: 使用Effect.all并行运行多个Effect。

好示例:

import { Effect, pipe } from "effect";

// 模拟从不同源获取数据
const fetchUser = Effect.succeed({ id: 1, name: "Alice" }).pipe(
  Effect.delay("100 millis")
);

const fetchPosts = Effect.succeed([
  { id: 1, title: "你好世界" },
  { id: 2, title: "Effect太棒了" },
]).pipe(Effect.delay("150 millis"));

const fetchSettings = Effect.succeed({ theme: "dark" }).pipe(
  Effect.delay("50 millis")
);

// 并行获取所有数据
const program = Effect.gen(function* () {
  const [user, posts, settings] = yield* Effect.all(
    [fetchUser, fetchPosts, fetchSettings],
    { concurrency: "unbounded" }
  );

  yield* Effect.log(`加载了 ${user.name},有 ${posts.length} 个帖子`);
  return { user, posts, settings };
});

Effect.runPromise(program);

原理:

使用Effect.all来并发运行多个Effect并等待它们全部完成。默认情况下,Effect顺序运行——添加concurrency选项以并行运行它们。

真实应用经常需要同时做多件事——从几个API获取数据、处理多个文件等。Effect.all让你自然地表达这一点,没有回调地狱或复杂的Promise.all模式。


为什么用Effect?Effect与Promise的比较

规则: 理解为什么Effect比原始Promise更好。

原理:

Effect解决了三个Promise没有的问题:

  1. 错误被类型化 - 你确切知道什么可能出错
  2. 依赖被追踪 - 你知道需要什么服务
  3. Effect是惰性的 - 在你说运行之前,什么都不运行