已发布 上游基线 bf46254 原文 ↗ 在 GitHub 编辑

重试

通过限制、条件、Schedule 与 fallback 来重试可恢复的类型化失败。

重试适用于临时性失败,例如临时的网络或服务不可用。它不能替代对永久性错误的处理,并且 defect 与中断永远不会被重试。

retry

Effect.retry 会在出现类型化失败之后重新运行一个 Effect。源 Effect 总是先被求值一次,然后才应用重试策略。

示例(重试固定次数)

import { Data, Effect } from "effect"

class TemporaryError extends Data.TaggedError("TemporaryError")<{
  readonly attempt: number
}> {}

let attempts = 0
const request = Effect.suspend(() => {
  attempts++
  return attempts < 3
    ? Effect.fail(new TemporaryError({ attempt: attempts }))
    : Effect.succeed("ok")
})

const program = request.pipe(Effect.retry({ times: 5 }))

Effect.runSync(program) // => "ok"
attempts // => 3

times 是初次尝试之后的重试次数。因此 { times: 5 } 最多允许源 Effect 执行六次。

只重试选定的错误

选项对象可以组合:

  • while:在谓词为 true 时重试;
  • until:在谓词为 true 时停止重试;
  • times:限制重试次数;
  • schedule:控制时序以及额外的停止条件。

谓词既可以返回布尔值,也可以返回一个 Effect。

示例(只重试临时性错误)

import { Data, Effect, Exit } from "effect"

class RequestError extends Data.TaggedError("RequestError")<{
  readonly retryable: boolean
}> {}

let attempts = 0
const request = Effect.failSync(() => {
  attempts++
  return new RequestError({ retryable: attempts < 2 })
})

const program = request.pipe(
  Effect.retry({
    times: 5,
    while: (error) => error.retryable,
  }),
)

Effect.runSyncExit(program) // => Exit.fail(new RequestError({ retryable: false }))
attempts // => 2

使用 Schedule

Schedule 可以定义延迟、退避、抖动以及重试上限。例如,Schedule.recurs(3) 允许在初次尝试之后重试三次。

import { Effect, Schedule } from "effect"

let attempts = 0
const request = Effect.suspend(() => {
  attempts++
  return attempts < 2 ? Effect.fail("temporary") : Effect.succeed("ok")
})

const program = request.pipe(Effect.retry(Schedule.recurs(3)))

Effect.runSync(program) // => "ok"

当重复取决于成功的值而不是错误时,请改用 Effect.repeat

retryOrElse

Effect.retryOrElse 使用一个 Schedule,并在该 Schedule 耗尽时运行一个 fallback Effect。这个 fallback 会接收最终的错误以及 Schedule 的输出。

示例(重试后进行 fallback)

import { Effect, Schedule } from "effect"

let attempts = 0
const request = Effect.failSync(() => {
  attempts++
  return "unavailable"
})

const program = Effect.retryOrElse(
  request,
  Schedule.recurs(2),
  (error, retries) => Effect.succeed(`${error} after ${retries} retries`),
)

Effect.runSync(program) // => "unavailable after 2 retries"
attempts // => 3