fp-ts的pipe中混合使用Either与TaskEither的异步问题及报错解决
核心问题拆解
- fp-ts的
Do/绑定语法要求整条链路在同一个monad上下文内,只要存在一个异步的TaskEither返回值,整个计算链都需要统一到TaskEither上下文。原有同步返回Either的函数不需要重写,用TE.fromEither就可以快速提升到TaskEither上下文。 TE.fold的两个入参函数都要求返回Task类型,你直接写返回void的函数不符合类型要求,所以会报编译错误。Task类型本质是() => Promise<T>的懒执行函数,你构造完之后没有主动调用,内部逻辑自然不会执行,所以没有控制台输出。
正确实现代码
方案1:保留原有同步函数,仅修改异步逻辑
import { pipe } from 'fp-ts/function' import * as E from 'fp-ts/Either' import * as TE from 'fp-ts/TaskEither' import * as T from 'fp-ts/Task' // 原有类型定义保持不变 interface Product { count: number pricePerItem: number } interface PTD { p: Product t: number d: number } // 原有同步函数完全不用修改 function getProduct(): E.Either<Error, Product> { return E.right({ count: 10, pricePerItem: 5 }) } function getTax(p: Product): E.Either<Error, number> { return E.right(p.pricePerItem * p.count * 0.085) } // 仅异步函数用TaskEither定义 function getDelivery(p: Product): TE.TaskEither<Error, number> { // 实际异步场景可以用TE.tryCatch包裹异步调用: // return TE.tryCatch(() => 你的异步方法(), E.toError) return TE.right(p.count * 0.05) } function run(): TE.TaskEither<Error, PTD> { return pipe( TE.Do, // 同步返回的Either用TE.fromEither提升到TaskEither上下文 TE.bind('p', () => TE.fromEither(getProduct())), TE.bind('tax', ({p}) => TE.fromEither(getTax(p))), // 异步函数直接绑定即可 TE.bind('delivery', ({p}) => getDelivery(p)), TE.map(({ p, tax, delivery }) => ({ p, t: tax, d: delivery })) ) } function main() { pipe( run(), TE.fold( // 直接返回符合Task类型的函数即可 e => () => Promise.resolve(console.log(`error: ${e}`)), it => () => Promise.resolve(console.log(`ok ${it.p.count} ${it.p.pricePerItem} ${it.t} ${it.d}`)) ) )() // 必须加()执行返回的Task函数,才会真正触发逻辑 } main()
方案2:全量改用TaskEither的情况
你更新后的代码只需要在main函数的pipe末尾加个()执行返回的Task,就可以正常输出:
function main() { pipe( run(), TE.fold( (e) => printError(e), (ptd) => printPTD(ptd) ) )() // 加这一行就有输出了 }
内容的提问来源于stack exchange,提问作者Kevin Le - Khnle
相关产品推荐
相关产品推荐

