Effect 的第一个优势是错误处理。
然而,Effect.promise 不处理错误。
const main = Effect.flatMap(
fetchRequest,
jsonResponse
);
Effect.runPromise(main);当你使用 Effect.promise 时,Effect 假设操作永远不会失败。实际上,main 的工作方式就像 Promise<unknown>,只是被包装在 Effect 中。
如果
Effect.promise内部的函数抛出异常,那么执行 Effect 也会抛出异常! 👇
> effect-getting-started-course@1.0.0 dev
> tsx src/index.ts
node:internal/process/promises:289
triggerUncaughtException(err, true /* fromPromise */);
^
SyntaxError: Unexpected token 'N', "Not Found" is not valid JSON由于我们知道 fetch 可能失败,我们需要另一个函数:Effect.tryPromise。
import { Effect } from "effect";
/// 👇 Effect<Response, UnknownException>
const fetchRequest = Effect.tryPromise(
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);虽然 Effect.promise 返回 Effect<Response>,但 Effect.tryPromise 会收集任何错误并返回 Effect<Response, UnknownException>。
错误类型
什么是 UnknownException?为什么 Effect 现在有 2 个泛型参数?
当你执行任何普通的 TypeScript 函数时,你无法知道可能出现什么问题,除非你阅读函数实现:
const request: Promise<Response> = fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");简单的例子:你怎么知道 fetch 是否会失败?request 是 Promise<Response>,所以不知道是否会出错,或者如何出错。
当你组合多个函数时,这个问题变得更加严重。
const main: Promise<unknown> =
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
.then((response) => response.json());由于 request 和 json 都可能失败,你只有两个选择:
- 阅读所有函数的实现并检查任何
throw,然后用 try/catch 包装每个函数
const main = async (): Promise<unknown> => {
let response;
try {
response = await fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
} catch (e) {
// fetch 错误:在这里做一些处理
return;
}
try {
return response.json();
} catch (e) {
// json 错误:在这里做一些处理
return;
}
};- 将所有内容包装在单个
try/catch中,并报告通用的”发生了某个错误”
const main = async (): Promise<unknown> => {
try {
const response = await fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
return response.json();
} catch (e) {
// 某个地方发生了错误 💁🏼♂️
return;
}
};Effect 通过在类型中直接提供错误来解决这个问题:
const fetchRequest: Effect<Response, UnknownException> = Effect.tryPromise(
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);当我们组合 fetchRequest 和 jsonResponse 时,我们得到一个返回 Effect<unknown, UnknownException> 的程序 main:
unknown是当一切正常工作时调用response.json()的返回类型UnknownException是当fetchRequest或jsonResponse失败时的错误类型
/// Effect<Response, UnknownException>
const fetchRequest = Effect.tryPromise(
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
/// Effect<unknown, UnknownException>
const jsonResponse = (response: Response) => Effect.tryPromise(
() => response.json()
);
/// Effect<unknown, UnknownException>
const main = Effect.flatMap(fetchRequest, jsonResponse);我们仍然无法区分
fetchRequest和jsonResponse中的错误,因为两者都被类型化为UnknownException。别担心!
tryPromise还允许定义自定义错误,我们将在后续课程中学习如何实现。