怎么用 catchReasons 处理 HttpClientError?
一次 HTTP 调用失败,可能是 404,可能是 500,也可能是根本没连上,而这三种在 HttpClientError 里是同一个外层错误,区别藏在它的 reason 里。
对 HttpClientError 用 Effect.catchReasons("HttpClientError", { StatusCodeError: ..., TransportError: ... }),按 reason 的 _tag 分别恢复,没写的 reason 保持失败。
HttpClientError 是 effect/http 里典型的「父错误 + tagged reason」形状,reason 包括 TransportError、StatusCodeError、DecodeError 等。这个错误在 effect 4.0.5 里标着 @stability unstable。catchReasons 的通用用法见Effect 怎么处理「父错误 + 嵌套 reason」的两级错误,下面只看 HTTP 这个具体场景。
怎么把 404 当作不存在、传输失败当作离线?
下面的例子先造一个能按状态码失败的 respond,再用 catchReasons 恢复:
import { Effect } from "effect"
import { HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http"
const request = HttpClientRequest.get("http://localhost/users/1")
const respond = (status: number): Effect.Effect<string, HttpClientError.HttpClientError> =>
status === 200
? Effect.succeed("ok")
: Effect.fail(
new HttpClientError.HttpClientError({
reason: new HttpClientError.StatusCodeError({
request,
response: HttpClientResponse.fromWeb(request, new Response(null, { status }))
})
})
)
export const program = (status: number) =>
respond(status).pipe(
Effect.catchReasons("HttpClientError", {
StatusCodeError: (r) => (r.response.status === 404 ? Effect.succeed("missing") : Effect.fail(r)),
TransportError: () => Effect.succeed("offline")
})
)我实际跑了 200、404、500 和一个 TransportError:分别得到 ok、missing、StatusCodeError 失败、offline。
处理器里 Effect.fail(r) 失败出去的是什么?
失败出去的是 reason(StatusCodeError)本身,而不是原来的 HttpClientError。处理器的失败类型会直接进入结果的 E,和父错误是否保留互不相干。所以 500 那个分支,调用方在 E 里看到的是 StatusCodeError。
另外,这个例子没有带兜底,父错误仍然留在 E 里,原因和解法见catchReasons 全部写完,为什么类型里还有父错误?。
AI 模块的 AiError 也能这样处理吗?
可以。AiError.AiError 的 reason 是 RateLimitError、QuotaExhaustedError、ContentPolicyError、InvalidRequestError、NetworkError 等十几个变体。我用 AiError.make 构造了一个限流错误,catchReason("AiError", "RateLimitError", ...) 和 unwrapReason("AiError") 都能通过 tsc 并给出正确的类型。读 retryAfter 时要注意它在 reason 上是可选的 Duration。
用在生产代码里要注意什么?
HttpClientError 和 AiError 本身是 unstable,字段和 reason 集合在后续版本可能调整,升级版本后要重新确认 reason 的 tag 名;catchReason 这一组组合子是 stable。reason 的 tag 写错会直接编译失败,这是升级时能提前发现变化的原因。
版本是 effect 4.0.5。本文的 HTTP 例子在这个版本上运行过 200、404、500 和TransportError四种输入;AiError部分只用tsc检查过类型,没有运行。HttpClientError与AiError属于 unstable,后续版本可能变化。