Categories
Tags
Ai 生成 API学习 API简化 api请求 API调用 best-practices Blogging Caching catchTag catchTags class CLI Config context Context Context.Tag CSS Customization Demo development DocC dual API effect Effect Effect.Service Effect.succeed Example extension filterOrFail flatMap Fuwari gen generator grep hooks HTML HTTP响应 IDE自动补全 iOS javascript JavaScript Javascript Layer.effect Layer.provide Layers Linux Markdown Mock Next.js ParseError pipe pokemon PostCSS process.env progress Promise promise provideService PWA react React React Hook Form React Query React Router react-native Scheduler Schema Schema.Class security Service Worker Services SSR state-management suspense Tagged Errors TaggedError TanStack Query TanStack Start tips tryPromise tsconfig TypeScript typescript Video VS Code vscode Web API Web Development yield Zod 不透明类型 二叉树 代码组织 任务调度 优先级 使用服务 依赖注入 依赖管理 值语义 入门教程 最佳实践 最小堆 函数式编程 函数组合 前端 前端开发 副作用 副作用控制 可视化 可组合性 可维护性 可访问性 命令行 响应过滤 多个错误 实现 实践指南 层 层依赖 层组合 工具链 并发控制 应用架构 延迟执行 开发技巧 开发教程 开源 异步处理 异步操作 异步编程 性能优化 手写系列 排序 接口设计 插件开发 数据结构 数据获取 数据解码 数据验证 无限滚动 日历 日志分析 服务 服务依赖 服务定义 服务实现 服务提供 测试 源码分析 状态管理 环境变量 生成器 离线支持 程序分离 算法 类型安全 类型定义 类型推断 类型系统 类定义 线性代码 组合 翻译 自定义错误 表单验证 记忆化 设计模式 语义化 运维 运行时验证 部分应用 配置 配置变量 配置服务 配置管理 重构 错误处理 错误定义 错误恢复 项目设置
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);
IMPORTANTEffect 组合模式
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/