You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将`(env)=> (args)=>TaskEither`转为ReaderTaskEither优化SPA缓存清理?

重构标签创建逻辑:用ReaderTaskEither优化环境对象传递

问题背景

在SPA中现有标签创建流程:

  • 定义UserId、User、TagType(可选值"NEED_HELP"/"NEED_STORAGE")、Tag、TagDraft等类型
  • 通过createTagDraft生成标签草稿,postTag提交至API,再用flow组合为createTagTask(返回TaskEither)

现在需要新增客户端标签缓存清理操作,当前通过createTagAndCleanTask直接传入queryCache实现,但希望改用ReaderTaskEither(RTE)优化环境对象传递,遇到以下问题:

  1. 使用RTE.fromTaskEither处理createTagTask时失败,因为createTagTask是返回TaskEither的函数,而非TaskEither实例
  2. 在flow中加入RTE.fromTaskEither时,无法正确配置类型与环境对象
  3. 不清楚如何通过flow将(env) => (args) => body的参数顺序反转为(args) => (env) => body

解决方案

1. 明确环境类型定义

首先定义包含queryCache的环境类型,让类型系统清晰约束环境对象:

import { ReaderTaskEither as RTE, TaskEither as TE } from 'fp-ts/lib';
import { QueryCache } from '@tanstack/react-query'; // 假设使用React Query的QueryCache

// 定义环境类型
type TagEnv = {
  queryCache: QueryCache;
};

// 定义标签创建的入参类型
type CreateTagArgs = {
  user: User;
  type: TagType;
};

2. 重构现有逻辑为RTE兼容形式

处理原有createTagTask

原有createTagTask不依赖环境,只需将其返回的TaskEither包装为RTE(忽略环境参数):

// 原有生成草稿逻辑
const createTagDraft = (user: User, type: TagType): TagDraft => {
  return { userId: user.id, type, createdAt: new Date() }; // 示例实现
};

// 原有提交API逻辑
const postTag = (draft: TagDraft): TE<ApiError, Tag> => {
  // 示例:调用API并转为TaskEither
  return TE.tryCatch(
    () => fetch('/api/tags', { method: 'POST', body: JSON.stringify(draft) }).then(res => res.json()),
    (err) => err as ApiError
  );
};

// 原有组合逻辑:返回TaskEither
const createTagTask = (args: CreateTagArgs): TE<ApiError, Tag> =>
  flow(
    ({ user, type }) => createTagDraft(user, type),
    postTag
  )(args);

// 转为RTE:接受入参,返回不依赖环境的RTE
const createTagRTE = (args: CreateTagArgs): RTE<TagEnv, ApiError, Tag> =>
  RTE.fromTaskEither(createTagTask(args));

实现缓存清理的RTE操作

缓存清理依赖环境中的queryCache,直接用RTE.asks获取环境并执行操作:

// 清理标签缓存的RTE操作
const cleanTagCache = (tag: Tag): RTE<TagEnv, never, void> =>
  RTE.asksIO((env) => {
    // 示例:失效指定用户的标签查询缓存
    env.queryCache.invalidateQueries({ queryKey: ['tags', tag.userId] });
  });

3. 组合创建与缓存清理逻辑

使用RTE的chainFirst组合两个操作(创建标签后清理缓存,保留标签结果):

// 最终组合逻辑:接受入参,返回包含环境依赖的RTE
const createTagAndCleanRTE = (args: CreateTagArgs): RTE<TagEnv, ApiError, Tag> =>
  flow(
    createTagRTE,
    RTE.chainFirst(cleanTagCache) // 创建成功后执行缓存清理,保留标签结果
  )(args);

4. 解决参数反转问题

如果原有代码是(env) => (args) => TaskEither的形式,可使用fp-ts的flip函数直接反转参数顺序,得到(args) => (env) => TaskEither(即RTE类型):

import { flip } from 'fp-ts/lib/function';

// 原有旧版逻辑:先传环境,再传入参
const oldCreateAndClean = (env: TagEnv) => (args: CreateTagArgs): TE<ApiError, Tag> =>
  TE.chainFirst(createTagTask(args), (tag) => 
    TE.fromIO(() => env.queryCache.invalidateQueries(['tags', tag.userId]))
  );

// 反转参数顺序:得到(args) => (env) => TE,即RTE<TagEnv, ApiError, Tag>
const createTagAndCleanRTE = flip(oldCreateAndClean);

5. 执行RTE逻辑

调用时传入入参和环境对象,执行最终的TaskEither:

// 示例:获取当前环境与入参
const currentEnv: TagEnv = { queryCache: window.queryCache };
const currentArgs: CreateTagArgs = { user: currentUser, type: "NEED_HELP" };

// 执行RTE
createTagAndCleanRTE(currentArgs)(currentEnv)
  .fold(
    (error) => console.error('标签创建失败:', error),
    (tag) => console.log('标签创建成功并清理缓存:', tag)
  )();

关键注意事项

  • ReaderTaskEither的本质是(env: E) => TaskEither<L, R>,所以只要函数符合这个签名,就是合法的RTE,无需额外包装
  • 使用flip函数时,确保原函数是柯里化的(即(a) => (b) => c的形式),否则无法正确反转参数
  • 组合RTE操作时,优先使用chain/chainFirst/map等RTE内置方法,而非直接嵌套flow,避免类型推断问题

内容的提问来源于stack exchange,提问作者xav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 18:10:48