fp-ts中pipe内函数未执行问题排查与语法修正求助
fp-ts pipe与TaskEither使用问题排查及修复
问题描述
作为函数编程新手,使用fp-ts的pipe函数时遇到异常:
- 调用包含
tryCatch的validateRegistrationForm函数时,tryCatch、chain或fold的逻辑从未执行,断点直接跳到后续代码 - 单独调用
validateRegistrationForm(user)并打印时,输出的是一个函数而非预期的TaskEither返回值
错误分析
- TaskEither的惰性执行特性:
TaskEither<E, A>本质是() => Promise<Either<E, A>>,属于惰性异步函数,直接打印会显示函数本身,必须调用它(加())才会触发执行。 - Joi验证逻辑错误:
schema.validateAsync验证失败时会直接抛出异常,而非返回包含error的对象,原代码中let {error} = await schema.validateAsync(user);的解构完全无效,无法捕获验证错误。 - 未执行最终的Task:
fold方法处理TaskEither后返回的是Task<void>(即异步函数),原代码中仅调用pipe但未执行这个最终的异步函数,导致整个流程没有实际运行。
修复方案
1. 修正Joi验证错误处理
去掉错误的解构逻辑,让tryCatch直接捕获validateAsync抛出的异常:
// 移除错误的{error}解构,直接等待validateAsync完成 await schema.validateAsync(user); // 验证通过直接返回user,失败则由tryCatch捕获异常
2. 执行最终的Task流程
在postUser中,pipe执行后得到的是一个异步函数,必须调用它才会触发整个验证、保存的流程:
// 调用pipe返回的异步函数,触发流程 pipe( // ... 原有逻辑 )();
3. 理解TaskEither的调用逻辑
如果需要单独测试validateRegistrationForm,需要调用返回的函数并处理Promise:
validateRegistrationForm(user)().then(either => { console.log(either); // 此时才能看到Either类型的结果 });
完整修正代码
修正后的validateRegistrationForm
const validateRegistrationForm = (user: User): TaskEither<Error, User> => tryCatch( async () => { const schema = Joi.object().keys({ userName: Joi.string().min(4).max(255).required(), firstName: Joi.string().pattern(new RegExp(/^[a-z\d\-_\s]+$/i)).min(1).max(50).required(), lastName: Joi.string().pattern(new RegExp(/^[a-z\d\-_\s]+$/i)).min(1).max(50).required(), emailAddress: Joi.string().email().required(), password: Joi.alternatives().try(Joi.string(), Joi.number()).required(), confirmPassword: Joi.alternatives().try(Joi.string(), Joi.number()).valid(Joi.ref('password')).required() // 原代码用disallow会要求确认密码与原密码不同,不符合注册逻辑,改为valid确保一致 }); // 验证失败直接抛出异常,由tryCatch捕获 await schema.validateAsync(user); return user; }, (reason: any) => reason instanceof Error ? reason : new Error(reason?.message || 'Validation failed') );
修正后的createUser
const createUser = (user: User): TaskEither<Error, User> => { return pipe( user, validateRegistrationForm, chain((validContent: User) => { // 无需重新构造user,直接传入validContent即可 return repository.saveUser(validContent); }) ); };
修正后的postUser
postUser: (req, res) => { // 调用pipe返回的异步函数,触发整个流程 pipe( req.body, interactor.createUser, fold( (error) => async () => { console.error(error); res.status(400).send({ error: error.message }); // 验证错误用400状态码更合理 }, (user) => async () => { res.status(201).send(user); // 创建成功用201状态码更规范 } ) )(); }
内容的提问来源于stack exchange,提问作者user1790300
相关产品推荐
相关产品推荐

