如何在fp-ts中让TaskEither忽略单个错误返回成功结果?
问题
我正在使用fp-ts库,需要并行调用两个可能失败的函数first和second,它们的返回类型都是TE.TaskEither<Error, User>。
当前我用TE.sequenceArray([first, second])调用,它的返回类型是TE.TaskEither<Error, ReadonlyArray<User>>,但这个方法是"快速失败"的——只要其中一个调用失败,整个结果就会直接返回Left,不符合我的需求。
我需要实现的逻辑是:
- 若两个调用都失败(无用户找到),返回
Left(Error("user does not exist")) - 只要有一个调用成功,就返回
Right(ReadonlyArray<User>)(包含1个或2个User) - 其他异常情况返回
Left(Error("internal server error"))
我尝试过以下代码,但发现if判断逻辑根本没执行:
F.pipe( TE.sequenceArray([first, second]), TE.chain(successes => { if (successes.length > 0) { return TE.right(successes); } else { return TE.left(Error()); } }), TE.map(results => results.flat()), );
解决方案
核心问题分析
TE.sequenceArray的特性是快速失败:只要数组中有任意一个TaskEither返回Left,整个sequenceArray就会直接返回Left,不会进入后续的TE.chain逻辑,这就是你的if判断没执行的原因。
要实现"允许单个失败,只在全失败时返回错误"的逻辑,需要先把每个任务的失败结果转换成成功容器内的"空值",再统一处理结果。
具体实现步骤
- 转换单个任务的错误处理:把每个
TaskEither<Error, User>转换成TaskEither<Error, Option<User>>——成功时返回Right(Some(user)),失败时返回Right(None)(这里只捕获"用户不存在"的错误,其他异常仍保留为Left)。 - 并行执行所有任务:用
TE.sequenceArray执行转换后的任务数组,得到所有结果的集合。 - 过滤有效结果并判断:从结果数组中提取出所有
Some(User)的项,再根据数量返回对应结果。
代码示例
import * as TE from 'fp-ts/TaskEither'; import * as O from 'fp-ts/Option'; import * as F from 'fp-ts/function'; import { filter } from 'fp-ts/ReadonlyArray'; // 假设User类型已定义 type User = { id: string; name: string }; // 转换单个任务:将失败(用户不存在)转为None,其他异常保留为Left const toOptionTask = (te: TE.TaskEither<Error, User>) => TE.fold( (err) => { // 假设"用户不存在"的错误可以通过err.message判断,根据实际情况调整 if (err.message.includes('user does not exist')) { return TE.right(O.none); } // 其他异常仍返回Left return TE.left(new Error('internal server error')); }, (user) => TE.right(O.some(user)) )(te); // 主逻辑 const getUsers = F.pipe( // 转换两个任务 [toOptionTask(first), toOptionTask(second)], TE.sequenceArray, TE.chain((results) => { // 过滤出存在的User const validUsers = filter(O.isSome)(results).map(O.getOrElse(() => undefined as User)); if (validUsers.length === 0) { return TE.left(new Error('user does not exist')); } return TE.right(validUsers); }) );
代码说明
toOptionTask函数:将任务的失败分为两种情况——"用户不存在"转为Right(None),其他异常直接返回Left,保证只有预期的失败会被纳入结果集合。TE.sequenceArray:此时因为每个任务的失败(用户不存在)都被转为Right(None),所以不管单个任务是否成功,sequenceArray都会返回Right(ReadonlyArray<Option<User>>),确保后续逻辑能执行。- 过滤与判断:从结果数组中提取有效User,空数组则返回"用户不存在"错误,否则返回有效User数组。
内容的提问来源于stack exchange,提问作者needith
相关产品推荐
相关产品推荐

