477 words
2 minutes
[Effect Services] 01. 重构Effect代码

重构Effect代码#

https://github.com/typeonce-dev/effect-getting-started-course

到目前为止,我们在单个文件 index.ts 中实现了所有内容。

这个单文件开始看起来复杂:

index.ts

import { Config, Data, Effect, Schema } from "effect";

class Pokemon extends Schema.Class<Pokemon>("Pokemon")({
  id: Schema.Number,
  order: Schema.Number,
  name: Schema.String,
  height: Schema.Number,
  weight: Schema.Number,
}) {}

class FetchError extends Data.TaggedError("FetchError")<{}> {}
class JsonError extends Data.TaggedError("JsonError")<{}> {}

const config = Config.string("BASE_URL");

const fetchRequest = (baseUrl: string) =>
  Effect.tryPromise({
    try: () => fetch(`${baseUrl}/api/v2/pokemon/garchomp/`),
    catch: () => new FetchError(),
  });

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

const decodePokemon = Schema.decodeUnknown(Pokemon);

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

  const json = yield* jsonResponse(response);

  return yield* decodePokemon(json);
});

const main = program.pipe(
  Effect.catchTags({
    FetchError: () => Effect.succeed("Fetch error"),
    JsonError: () => Effect.succeed("Json error"),
    ParseError: () => Effect.succeed("Parse error"),
  })
);

Effect.runPromise(main).then(console.log);
TIP

代码组织的重要性

我们希望更好地组织我们的代码,使其更易于维护和测试。这就是 Effect 的真正力量:可组合性

我们通过创建所谓的服务来实现这一点。

重构:使代码更简洁#

NOTE

重构策略

在转向服务之前,重构代码是一个好主意(这将有助于稍后理解服务):

  • 我们可以将多个独立的函数收集到 .gen 内部,而不是拥有多个独立的函数

  • program 重命名为 getPokemon

const getPokemon = Effect.gen(function* () {
  const baseUrl = yield* Config.string("BASE_URL");

  const response = yield* Effect.tryPromise({
    try: () => fetch(`${baseUrl}/api/v2/pokemon/garchomp/`),
    catch: () => new FetchError(),
  });

  if (!response.ok) {
    return yield* new FetchError();
  }

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

  return yield* Schema.decodeUnknown(Pokemon)(json);
});
IMPORTANT

代码模块化

现在很清楚,这个单一的 Effect 不是我们的完整程序,而只是我们代码库中的一个 API。

让我们也将错误和 Schema 移动到它们自己的独立文件中:

errors.ts

import { Data } from "effect";

export class FetchError extends Data.TaggedError("FetchError")<{}> {}
export class JsonError extends Data.TaggedError("JsonError")<{}> {}

schemas.ts

import { Schema } from "effect";

export class Pokemon extends Schema.Class<Pokemon>("Pokemon")({
  id: Schema.Number,
  order: Schema.Number,
  name: Schema.String,
  height: Schema.Number,
  weight: Schema.Number,
}) {}

我们现在将在服务内部组织 Pokémon API。

[Effect Services] 01. 重构Effect代码
https://0bipinnata0.my/posts/course/effect-beginners-complete-getting-started/effect-services/01-refactoring-effect-code/
Author
0bipinnata0
Published at
2025-08-30 17:35:57