在 redux-observable 中编写和排序多个史诗

2024-01-09

我有一个问题不知道如何解决。

我有两个史诗向 api 发出请求并更新商店:

const mapSuccess = actionType => response => ({
  type: actionType + SUCCESS,
  payload: response.response,
});

const mapFailure = actionType => error => Observable.of({
  type: actionType + FAILURE,
  error,
});

const characterEpic = (action$, store) =>
  action$.ofType(GET_CHARACTER)
    .mergeMap(({ id }) => {
      return ajax(api.fetchCharacter(id))
        .map(mapSuccess(GET_CHARACTER))
        .catch(mapFailure(GET_CHARACTER));
    });

const planetsEpic = (action$, store) =>
  action$.ofType(GET_PLANET)
    .mergeMap(({ id }) => {
      return ajax(api.fetchPlanet(id))
        .map(mapSuccess(GET_PLANET))
        .catch(mapFailure(GET_PLANET));
    });

现在我有一个简单的场景,我想创建结合上面两个操作的第三个操作,我们称之为fetchCharacterAndPlanetEpic。我该怎么做? 我认为在很多情况下(在我的情况下),在第二个操作开始之前将第一个操作的结果发送到商店非常重要。这对于 Promise 来说可能是微不足道的redux-thunk,但我无法想出一种方法来做到这一点rxjs and redux-observable.

Thanks!


Tomasz的答案有效并且有优点和缺点(最初是在redux-observable#33 https://github.com/redux-observable/redux-observable/issues/33#issuecomment-239329023)。一个潜在的问题是它使测试变得更加困难,但并非不可能。例如您可能必须使用依赖项注入来注入分叉史诗的模拟。

在看到他之前我已经开始输入答案了,所以我想我不妨将其发布给后代,以防任何人都感兴趣。

我之前也回答过另一个非常相似的问题,可能会有所帮助:如何延迟一个史诗直到另一个史诗发出值 https://stackoverflow.com/questions/42426908/how-to-delay-one-epic-until-another-has-emitted-a-value


我们可以发出getCharacter(),然后等待匹配GET_CHARACTER_SUCCESS在我们发出之前getPlanet().

const fetchCharacterAndPlanetEpic = (action$, store) =>
  action$.ofType(GET_CHARACTER_AND_PLANET)
    .mergeMap(({ characterId, planetId }) =>
      action$.ofType(GET_CHARACTER_SUCCESS)
        .filter(action => action.payload.id === characterId) // just in case
        .take(1)
        .mapTo(getPlanet(planetId))
        .startWith(getCharacter(characterId))
    );

这种方法的一个潜在的负面影响是,理论上GET_CHARACTER_SUCCESS这部史诗般的作品可能与我们等待的完全不同。过滤器action.payload.id === characterIdcheck 可以最大程度地保护您免受这种情况的影响,因为如果它具有相同的 ID,那么它是否属于您可能并不重要。

要真正解决这个问题,您需要某种独特的交易跟踪。我个人使用自定义解决方案,其中涉及使用辅助函数来包含唯一的事务 ID。像这样的东西:

let transactionID = 0;

const pend = action => ({
  ...action,
  meta: {
    transaction: {
      type: BEGIN,
      id: `${++transactionID}`
    }
  }
});

const fulfill = (action, payload) => ({
  type: action.type + '_FULFILLED',
  payload,
  meta: {
    transaction: {
      type: COMMIT,
      id: action.meta.transaction.id
    }
  }
});

const selectTransaction = action => action.meta.transaction;

然后它们可以像这样使用:

const getCharacter = id => pend({ type: GET_CHARACTER, id });

const characterEpic = (action$, store) =>
  action$.ofType(GET_CHARACTER)
    .mergeMap(action => {
      return ajax(api.fetchCharacter(action.id))
        .map(response => fulfill(action, payload))
        .catch(e => Observable.of(reject(action, e)));
    });

const fetchCharacterAndPlanetEpic = (action$, store) =>
  action$.ofType(GET_CHARACTER_AND_PLANET)
    .mergeMap(action =>
      action$.ofType(GET_CHARACTER_FULFILLED)
        .filter(responseAction => selectTransaction(action).id === selectTransaction(responseAction).id)
        .take(1)
        .mapTo(getPlanet(action.planetId))
        .startWith(getCharacter(action.characterId))
    );

关键细节是初始“挂起”操作在元对象中保存唯一的事务 ID。因此,初始操作基本上代表待处理的请求,然后在有人想要履行、拒绝或取消它时使用。fulfill(action, payload)

Our fetchCharacterAndPlanetEpic代码有点冗长,如果我们使用这样的东西,我们会做很多事情。因此,让我们创建一个自定义运算符来为我们处理这一切。

// Extend ActionsObservable so we can have our own custom operators.
// In rxjs v6 you won't need to do this as it uses "pipeable" aka "lettable"
// operators instead of using prototype-based methods.
// https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md
class MyCustomActionsObservable extends ActionsObservable {
  takeFulfilledTransaction(input) {
    return this
      .filter(output =>
        output.type === input.type + '_FULFILLED' &&
        output.meta.transaction.id === input.meta.transaction.id
      )
      .take(1);
  }
}
// Use our custom ActionsObservable
const adapter = {
  input: input$ => new MyCustomActionsObservable(input$),
  output: output$ => output$
};
const epicMiddleware = createEpicMiddleware(rootEpic, { adapter });

然后我们可以在我们的史诗中干净利落地使用该自定义运算符

const fetchCharacterAndPlanetEpic = (action$, store) =>
  action$.ofType(GET_CHARACTER_AND_PLANET)
    .mergeMap(action =>
      action$.takeFulfilledTransaction(action)
        .mapTo(getPlanet(action.planetId))
        .startWith(getCharacter(action.characterId))
    );

这里描述的事务式解决方案是真正的实验性。在实践中,多年来我注意到它有一些缺点,但我还没有抽出时间去考虑如何解决它们。也就是说,总的来说,它对我的​​应用程序非常有帮助。事实上,它也可以用来做乐观更新和回滚!几年前,我将这个模式和可选的乐观更新内容放入库中redux-事务 https://github.com/jayphelps/redux-transaction但我从来没有回过头来给予它一些爱,所以使用风险自负。即使我可能会回来,它也应该被视为放弃。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 redux-observable 中编写和排序多个史诗 的相关文章

  • Javascript 闭包 - 变量范围问题

    我正在阅读 Mozilla 开发者网站上有关闭包的内容 我注意到在他们的常见错误示例中 他们有以下代码 p Helpful notes will appear here p p E mail p
  • 在 IE8 中使用 javascript __proto__

    你好 我在 javascript 中有这两个对象 var john firstname John lastname Smith var jane firstname Jane 这样做 jane proto john 我可以访问 Jane 的
  • 获取 CRM 2011 中功能区按钮的 ID

    我创建了一个 JavaScript 我想在其中隐藏功能区Reactivate Lead按钮取决于某些条件 我通过在表单上按 F12 获得了按钮的 ID 即lead NoRelationship Form Mscrm Form lead Re
  • 使用非常大的背景位置偏移是否存在性能问题?

    我正在构建一个进度条控件 并且正在研究它实际上并不显示进度 而只是旋转 正在发生某事 的指示器的情况 我的设计基本上是交替的对角条纹 本质上是一个像这样的理发杆 但是 旋转 由于希望将尽可能多的负载转移给渲染引擎 我想为此使用 CSS 过渡
  • 多次训练brain.js?

    在第一次训练后 如何将新信息 仅新信息 而不是所有信息 因为这会花费太多性能 训练到我的用 Brain js 制作的神经网络 它有点粗糙 但您可以使用以下结构来实现 如果我们加入 2 个训练数据集 旧数据集与新数据集 然后重新训练keepN
  • 如何使传单圆圈标记可拖动?

    使用传单 我创建了一个L circleMarker我希望它是可拖动的 var marker L circleMarker new L LatLng 48 94603 2 25912 draggable true bindPopup Circ
  • JavaScript 可以检测用户的浏览器是否支持 gzip 吗?

    我可以使用 JavaScript 来检测用户的浏览器是否支持 gzip 压缩内容 客户端 而不是 Node js 或类似内容 我正在尝试支持以下边缘情况 有很多可能的文件可以加载到特定的 Web 应用程序上 最好在应用程序运行时根据需要加载
  • Twitter Bootstrap - 下拉菜单 - 箭头键不适用于 Firefox 中的输入标签

    要求 我想在带有用户名和密码字段的下拉菜单中放置一个登录表单 我可以做到这一点 除了以下问题之外 一切正常 Issue 打字时我无法使用箭头键 上 下 firefox 当输入位于下拉代码之外时 这很有效 这适用于其他浏览器 例如 googl
  • 使用 Javascript/Node.js 在代码内执行 mongoimport

    node js javascript 中是否有任何库可供个人使用mongoimport在代码中 据我了解 mongoimport 有点像 exe 您必须先执行它 然后才能使用其文本输入环境 是否可以在我的代码中执行 mongoimport
  • IE localStorage 事件失火

    在 Internet Explorer 9 和 10 中 localStorage 实现意外地触发事件 这里有很棒的线索 Chrome 的 localStorage 实现存在错误 https stackoverflow com questi
  • Javascript location.href 到 mailto 触发 GET HTTP,该 HTTP 在 Chrome 中被取消

    我有一个按钮可以触发以下 javascript 函数 function sendEmail var mail mailto email protected cdn cgi l email protection location href m
  • 在 javascript 中实现固定位置会导致 Safari 滚动时出现抖动

    固定位置不适用于我的用例 因为它固定在浏览器窗口上 您可能会处于文本在屏幕右侧之外且无法到达的状态 无论如何 我尝试使用绝对定位 然后调整javascript中的 顶部 它在 Firefox 和 Chrome 中运行良好 但在 Safari
  • ES6 静态方法引用 self? [复制]

    这个问题在这里已经有答案了 我有两节课 存储库和用户存储库 我想在 Repository 中定义一个静态方法 该方法在运行时调用 UserRepository 中的静态函数 有什么干净的方法可以做到这一点吗 class Repository
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 如何使用 Javascript OAuth 库不暴露您的密钥?

    看着Twitter OAuth 库 https dev twitter com docs twitter libraries 我看到了这个注释 将 JavaScript 与 OAuth 结合使用时要小心 不要暴露你的钥匙 然后 看着jsOA
  • 如果 jquery 验证激活,如何在单选按钮中放置红色边框[重复]

    这个问题在这里已经有答案了 我的问题是 如果 jquery 验证像示例图片中那样激活 我无法使单选按钮具有红色边框 任何人都可以帮我解决这个问题吗 http i38 photobucket com albums e149 eloginko
  • 如何将数据推送到嵌套对象

    如何将另一个元素推入variables来自以下对象的属性 var request name Name id 3 rules name Rule name tags tagId 1 variables variable var1 matchT
  • VS Code 扩展 - 获取完整路径

    我正在为 VS Code 编写一个插件 我需要知道调用扩展的文件的路径 无论是从编辑器上下文菜单或资源管理器上下文菜单调用还是用户只需键入扩展命令 function activate context get full path of the
  • 在 Firestore 文本字段中存储文本文件并删除换行符

    我正在尝试将 CSV 文件存储在 Cloud Firestore 内的文本字段中 然而 Firestore 正在删除所有换行符并将整个 CSV 文件存储为一行 这Firestore 数据类型文档 https firebase google
  • 使用
    元素作为 JavaScript 代码的输入。这是最好的方法吗?

    各位 显然 我是编码新手 所以最近完成了一些有关 HTML 和 Javascript 的 Lynda 课程后 我的简单 HTML 页面遇到了困难 基本上 我想要的是使用 JavaScript 进行基本计算 让用户使用 HTML 输入两个数字

随机推荐

  • 自动使基构造函数在派生类中可用?

    我有一个带有两个构造函数的基类 需要一个参数 public abstract class StoreBase private readonly SomeObject sobj protected StoreBase SomeObject s
  • 您重构代码的方式和频率如何?

    我的问题模糊地涉及this one https stackoverflow com questions 140677 how often should you refactor 但是 它不涉及技术或实践 我在读务实的程序员并且它强烈提倡尽可
  • 将大型 JS blob 传递给 Blazor byte[]

    我需要使用录制一些音频 甚至视频媒体API https developer mozilla org en US docs Web API MediaRecorder in JS example https mbuotidem github
  • 在生产中部署 Spring Boot

    我们有一个 Spring Boot Angular 应用程序 目前我们正在将其打包为 jar 并运行它 可以将包装用作 JAR 吗 或者我们应该转向 WAR 类型的包装 会有什么不同呢 除了部署方式之外 没有其他区别 使用 JAR 时 Sp
  • GridX启动后如何重新计算列宽?

    根据文档 https github com oria gridx wiki Create the Simplest Gridx https github com oria gridx wiki Create the Simplest Gri
  • SVG animateTransform 平移和缩放同时失败

    我有一条想要使用的动画路径animateTransform 我想同时平移和缩放路径 这不起作用 显然只有第二个动画起作用 在这种情况下scale 我究竟做错了什么 svg width 300px border 1px solid fill
  • 在ubuntu Linux服务器上通过命令行运行php文件

    我试图在 ubuntu Linux 服务器上运行 php 文件 但当我运行 php file name php 时出现 找不到命令 错误 在网上搜索时 我发现一篇文章建议我运行 sudo aptitude install php5 cli
  • InterfaceBuilder NSButton 连接到 NSViewController 原因“无法连接操作,目标类 NSObject 不响应”

    我有一个基于视图的NSTableView其 文件的所有者 是ViewController 我在自定义上拖动了一个按钮 用于删除 NSTableCellView 所以我想单击按钮并从 tableView 中删除当前行 但是当添加行时调试控制台
  • 隐藏用于 editText 焦点/触摸的软键盘

    我试图隐藏软键盘 使其在 EditText 接收焦点或触摸事件时不显示 我希望 EditText 是可编辑的 EditText 中会有文本 因此我希望 EditText 响应触摸事件 以便定位光标 但我将有专用的编辑按钮 而不是使用软键盘
  • 使用 Jasmine 测试时,Angularjs 注入控制器为空

    我目前正在与Angular 以及使用 Karma 和 Jasmine进行测试 例如 过滤器被注入到主模块中 并且可以毫无问题地进行测试 但是当我尝试测试控制器时 我得到了一个空物体注射后 这是我的主模块的代码 function use st
  • 具有固定滚动行和固定滚动列的大型动态大小 html 表格

    我需要在网页上显示一个大表格 并且需要防止第一列和第一行滚动 我想动态设置此表的垂直大小 在某些静态大小的页眉 页脚页面内容之间 以使其尽可能高 而不强制浏览器窗口具有垂直滚动条 browser window fixed static we
  • SwiftUI 安排本地通知没有按钮?

    这可能有一个非常简单的答案 因为我对 Swift 和 SwiftUI 还很陌生 并且刚刚开始学习 我正在尝试安排每天在特定时间重复的本地通知 但仅在选择切换时才执行此操作 因此 如果变量为真 我希望安排该通知 我在网上看了一些教程 比如这个
  • POST 字典为 JSON

    我需要将字典中的一些字段 来自 VB Net 应用程序 以 JSON 格式发送到 Web 服务 下面是代码 Dim dict As New Dictionary Of String String dict Add Drinks 2 dict
  • Fortran 中是否可以将零值变量输出为空白?

    我想在格式化文件中输出实际变量 如果变量非零 则使用格式语句 但如果变量为零 则仅输出空格 类似于 Iw 0 的做法 是否可以在格式语句中执行此操作 谢谢 不 不是使用格式语句 但是通过将值写入字符串并进行处理 这相当容易做到 下面是一个演
  • 如何制作一个极其广泛的情节

    我有一个包含 10000 个观察值的长时间序列 我想将其可视化 问题是 如果我只是正常地绘制它 时间维度将被压缩 并且我想要可视化的时间序列的任何细节都不会明显 例如 plot sin 1 10000 100 rnorm 10000 5 t
  • 具有私有 IP 的 Kubernetes 入口控制器

    是否可以在没有公共 IP 地址的情况下部署入口控制器 nginx Thanks 是否可以在没有公共 IP 地址的情况下部署入口控制器 nginx 毫无疑问 是的 如果 Ingress 控制器的Service is of type NodeP
  • 如何显示 url 中的图像?

    我有一个字符串变量tmpImgURLStr其中包含类似的 URLwww abc com img png 我想在我的 imageView 中显示该图像 因为我使用了一些代码 但它不起作用 如下所示 NSLog Img URL tmpImgUR
  • C++:按值将对象传递给同一类的成员函数

    我是 C 的初学者 刚刚开始学习 OOP 在下面的程序中 我添加了相同类的对象并显示了结果 但是 我无法理解这样一个事实 如果我按值将对象传递给函数 那么更改如何反映在调用函数中 这addNumbers 函数需要 Complex 类的两个对
  • 如何在test-cafe中使用数据发出post请求?

    我是 api 测试的初学者 我正在使用test cafe我已经写了测试来制作GET请求使用请求钩子工作正常 我能够获取数据 但是当我尝试制作时POST请求使用相同的请求钩子并且我在发出请求时无法发送数据 因为它需要是缓冲区类型 我无法将 J
  • 在 redux-observable 中编写和排序多个史诗

    我有一个问题不知道如何解决 我有两个史诗向 api 发出请求并更新商店 const mapSuccess actionType gt response gt type actionType SUCCESS payload response