489 words
2 minutes
[使用生成器进行组合] 02. 分离程序定义和错误处理
2025-08-30 16:49:06
2025-12-24 23:45:46

分离程序定义和错误处理#

你可能已经注意到 .gen 代码中缺少了 catchTags

const main = Effect.gen(function* () {
  const response = yield* fetchRequest;
  if (!response.ok) {
    return yield* new FetchError();
  }

  return yield* jsonResponse(response);
});

将程序的实现与错误处理分离是一个好的实践。这样我们可以专注于逻辑,然后基于类型签名来处理错误:

  • program 包含核心实现(类型为 Effect<unknown, FetchError | JsonError>
  • main 在错误处理后_派生_自 program(类型为 Effect<unknown, never>注意错误类型为 never
/// 👇 核心实现
const program = Effect.gen(function* () {
  const response = yield* fetchRequest;
  if (!response.ok) {
    return yield* new FetchError();
  }

  return yield* jsonResponse(response);
});

/// 👇 错误处理
const main = program.pipe(
  Effect.catchTags({
    FetchError: () => Effect.succeed("Fetch error"),
    JsonError: () => Effect.succeed("Json error"),
  })
);

进行错误处理最常见和建议的方式是在 program 后使用 pipe 操作符。


此时我们有以下代码:

index.ts

import { Data, Effect } from "effect";

/** 错误定义 **/
class FetchError extends Data.TaggedError("FetchError")<{}> {}
class JsonError extends Data.TaggedError("JsonError")<{}> {}


/** 实现部分 **/
const fetchRequest = Effect.tryPromise({
  try: () => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/"),
  catch: () => new FetchError(),
});

const jsonResponse = (response: Response) =>
  Effect.tryPromise({
    try: () => response.json(),
    catch: () => new JsonError(),
  });

const program = Effect.gen(function* () {
  const response = yield* fetchRequest;
  if (!response.ok) {
    return yield* new FetchError();
  }

  return yield* jsonResponse(response);
});


/** 错误处理 **/
const main = program.pipe(
  Effect.catchTags({
    FetchError: () => Effect.succeed("Fetch error"),
    JsonError: () => Effect.succeed("Json error"),
  })
);


/** 运行 Effect **/
Effect.runPromise(main).then(console.log);

Effect Playground

通过这种方式,我们能够在 .gen 内部使用线性代码实现逻辑,跟踪所有错误,然后单独处理它们。一切都是类型安全的,因为所有错误都在 Effect 的类型中报告。

等等,我们是不是忘记了什么?

main 的返回类型仍然是 Effect<unknown>。为什么是 unknown?我们在这里寻找的是一个 Pokémon!

我们如何确保 unknown 实际上是一个有效的 Pokémon?

事实证明,这个问题在纯 TypeScript 中是常见且不容易解决的

让我们跳到下一个模块,学习如何使用 Schema 来解决这个问题。

[使用生成器进行组合] 02. 分离程序定义和错误处理
https://0bipinnata0.my/posts/course/effect-beginners-complete-getting-started/gen-composition-using-generators/02-separate-program-definition-from-error-handling/
Author
0bipinnata0
Published at
2025-08-30 16:49:06