528 words
3 minutes
[Effect Services] 05. 提供服务以运行Effect

提供服务以运行Effect#

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

TIP

服务实现提供

与错误类似,我们可以通过在运行 Effect 之前提供具体实现来从类型中移除依赖。

我们到达了实际需要创建服务具体实现的点。使用 Context 创建的服务有一个 .of 方法,允许创建服务的实例。

还记得我们之前重构的 getPokemon 函数吗?让我们用它来创建 PokeApi 服务:

PokeApi.ts

export const PokeApi = Context.GenericTag<PokeApi>("PokeApi"); // 👉 `PokeApi.of` defines a concrete implementation for the service export const PokeApiLive = PokeApi.of({ 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); }), });

我们可以通过使用 .of 从同一个抽象服务创建任意数量的具体实现。

按照约定,生产实现有一个 -Live 后缀。如果需要,你可以创建 -Test/-Mock/-Dev 实现。

export const PokeApi = Context.GenericTag<PokeApi>("PokeApi");> export const PokeApiTest = PokeApi.of({ getPokemon: Effect.succeed({ id: 1, height: 10, weight: 10, order: 1, name: "myname", }), });

现在我们需要向 program 提供 PokeApiLive 来运行应用。我们使用 Effect.provideService 来做到这一点:

  • 第一个参数是服务 Context 定义

  • 第二个参数是服务的具体实现

index.ts

import { Effect } from "effect"; import { PokeApi, PokeApiLive } from "./PokeApi"; const program = Effect.gen(function* () { const pokeApi = yield* PokeApi; return yield* pokeApi.getPokemon; }); const runnable = program.pipe(Effect.provideService(PokeApi, PokeApiLive));

runnable 的类型现在是 Effect<Pokemon, FetchError | JsonError | ParseError | ConfigError, never>:我们提供了 PokeApi 的依赖,将其从类型中移除,现在第三个参数是 never

我们现在可以组合完整的应用并无错误地运行它:

index.ts

import { Effect } from "effect"; import { PokeApi, PokeApiLive } from "./PokeApi"; const program = Effect.gen(function* () { const pokeApi = yield* PokeApi; return yield* pokeApi.getPokemon; }); const runnable = program.pipe(Effect.provideService(PokeApi, PokeApiLive)); const main = runnable.pipe( Effect.catchTags({ FetchError: () => Effect.succeed("Fetch error"), JsonError: () => Effect.succeed("Json error"), ParseError: () => Effect.succeed("Parse error"), }) ); Effect.runPromise(main).then(console.log);
IMPORTANT

Effect 组合模式

  • program:完整的 Effect 实现,类型中包含错误和依赖

  • runnable:向 program 提供所有依赖,使第三个类型参数为 never

  • main:处理来自 runnable 的所有(或部分)错误,使第二个类型参数为 never

[Effect Services] 05. 提供服务以运行Effect
https://0bipinnata0.my/posts/course/effect-beginners-complete-getting-started/effect-services/05-providing-services-to-run-effects/
Author
0bipinnata0
Published at
2025-08-30 17:48:35