chengaofeng
发布于 2025-01-19 / 5 阅读
0
0

fp-ts的Task、TaskEither、TaskOption、TaskThese的区别与适用场景

fp-ts 中这几个 Task 相关类型的区别和使用场景:

Task<A>

interface Task<A> {

  (): Promise<A>

}
  • 最基础的异步计算类型

  • 表示一个必定会成功返回类型 A 的异步操作

  • 适用场景:做简单的异步操作,比如:

    • 获取当前时间

    • 生成随机数

    • 纯计算操作

TaskEither<E, A>

interface TaskEither<E, A> extends Task<Either<E, A>> {}
  • 可能失败的异步计算

  • 要么返回错误 E,要么返回成功值 A

  • 适用场景:可能出错的异步操作,如:

    • HTTP 请求

    • 文件操作

    • 数据库操作

TaskOption<A>

interface TaskOption<A> extends Task<Option<A>> {}
  • 可能为空的异步计算

  • 要么返回 Some(A),要么返回 None

  • 适用场景:可能返回空值的异步操作,如:

    • 查询可能不存在的数据

    • 解析可能失败但不关心失败原因的操作

TaskThese<E, A>

interface TaskThese<E, A> extends Task<These<E, A>> {}
  • 异步计算可能同时包含错误和成功值

  • 三种可能:left(E)、right(A)、both(E,A)

  • 适用场景:

    • 需要收集所有错误但仍继续执行的场景

    • 验证操作需要返回警告和数据

    • 批处理操作需要返回成功和失败的结果

选择指南

  1. 如果异步操作:

    • 一定成功 -> 使用 Task

    • 可能失败需要错误处理 -> 使用 TaskEither

    • 可能为空值 -> 使用 TaskOption

    • 需要同时处理错误和成功 -> 使用 TaskThese

  2. 如果需要:

    • 详细的错误信息 -> TaskEither

    • 只关心有无值 -> TaskOption

    • 收集多个验证错误 -> TaskThese

例子:

// Task

const getCurrentTime: Task<number> = () => 

  Promise.resolve(Date.now())

// TaskEither  

const fetchUser: TaskEither<Error, User> = (id) =>

  pipe(

    http.get(`/api/users/${id}`),

    map(response => response.data)

  )

// TaskOption

const findUser: TaskOption<User> = (id) =>

  pipe(

    fetchUser(id),

    map(O.fromNullable) 

  )

// TaskThese  

const validateUsers: TaskThese<string[], User[]> = (users) =>

  pipe(

    users,

    map(validateUser),

    sequence(T.ApplicativePar)

  )

希望这能帮助您理解这些类型的区别和如何选择!


评论