441 words
2 minutes
[使用生成器进行组合] 01. gen: 使用 Effect 编写线性代码
2025-08-30 16:35:30
2025-12-24 23:45:46

随着我们向 pipe 添加更多步骤,代码开始变得不太易读。

const main = fetchRequest.pipe(
  Effect.filterOrFail(
    (response) => response.ok,
    () => new FetchError()
  ),
  Effect.flatMap(jsonResponse),
  Effect.catchTags({
    FetchError: () => Effect.succeed("Fetch error"),
    JsonError: () => Effect.succeed("Json error"),
  })
);

我们从简单的 if 检查,变成了看起来可怕的带有两个参数和更多代码行的 filterOrFail

不要害怕!Effect 中有一个解决方案叫做 Effect.gen

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

  return yield* jsonResponse(response);
});

如果你仔细观察,这与普通的 async/await TypeScript 函数非常相似:

const fetchRequest = () => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
const jsonResponse = (response: Response) => response.json();

const main = async () => {
  const response = await fetchRequest();
  if (!response.ok) {
    throw new FetchError();
  }

  return await jsonResponse(response);
};

采用这种方式并进行以下更改:

  • async 替换为 Effect.gen

  • 将箭头函数 () => 替换为生成器函数 function* ()

  • awaitthrow 替换为 yield*

const main = async () => {
  const response = await fetchRequest();
  if (!response.ok) {
    throw new FetchError();
  }

  return await jsonResponse(response);
};

// 👆 找出差异 👇

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

  return yield* jsonResponse(response);
});

生成器的工作原理#

使用 Effect 时无需学习如何工作的细节。实际上,这只是一个实现细节。

作为参考,Effect.gen 使用了一个名为生成器函数的 JavaScript 特性。

这与迭代器协议结合使用,允许 Effect 在函数的每一步跟踪错误和成功。

对我们而言,这是一种编写更简洁、更美观的 Effect 代码的便捷方式,同时保持错误跟踪的所有好处

我建议在大多数用例中优先使用 Effect.gen 而不是 pipe。这也是我在代码中的做法 💁🏼‍♂️

从现在开始,我们将尽可能使用 Effect.gen 而不是 pipe

[使用生成器进行组合] 01. gen: 使用 Effect 编写线性代码
https://0bipinnata0.my/posts/course/effect-beginners-complete-getting-started/gen-composition-using-generators/01-gen-write-linear-code-with-effect/
Author
0bipinnata0
Published at
2025-08-30 16:35:30