我需要多次查询设备。每个查询都需要是异步的,并且设备一次不支持同时查询。 而且,一旦查询,就不能立即再次查询。它需要至少1秒的暂停才能正常工作。
我的两个查询由saveClock()
和saveConfig()
执行,返回一个Promise,并通过按预期返回undefined来解析。
在以下代码中,为什么删除take()
会阻止调用toArray()
?
这里发生了什么,是否有更好的方法来实现相同的行为?
export const saveEpic = (action$, store) =>
action$.ofType(SAVE)
.map(action => {
// access store and create object data
// ...
return data;
})
.mergeMap(data =>
Rx.Observable.from([
Rx.Observable.of(data).mergeMap(data => saveClock(data.id, data.clock)),
Rx.Observable.timer(1000),
Rx.Observable.of(data).mergeMap(data => saveConfig(data.id, data.config)),
Rx.Observable.of(data.id)
])
)
.concatAll()
.take(4)
.toArray()
// [undefined, 0, undefined, "id"]
.map(x => { type: COMPLETED, id: x[3] });
答案 0 :(得分:2)
我看到了几件事:
您的最终.map()
缺少括号,其当前形式是语法错误,但微妙的更改可能会使其意外地成为labeled statement而不是返回对象。因为它目前的形式是一个语法错误,我想这只是这篇文章中的一个错误,而不是你的代码(它甚至不会运行),而是仔细检查!
// before
.map(x => { type: COMPLETED, id: x[3] });
// after
.map(x => ({ type: COMPLETED, id: x[3] }));
修复后,该示例确实运行了一个简单的redux-observable测试用例:http://jsbin.com/hunale/edit?js,output所以,如果没有什么值得注意的,我做的与你不同,问题出现到在未提供的代码中。随意添加更多洞察力甚至更好,在我们的JSBin / git repo中重现它。
你没有提到的一件事,但非常值得注意的是,在redux-observable中,你的史诗通常是长寿的过程经理"。这个史诗实际上只会处理其中一个保存,然后是complete(),这可能不是你真正想要的?用户每个应用程序启动只能保存一次吗?似乎不太可能。
相反,您希望通过将此逻辑封装在mergeMap
中来保持顶级流您的史诗返回活动并监听将来的操作。 take(4)
并传递data.id
然后变得无关:
const saveEpic = (action$, store) =>
action$.ofType(SAVE)
.mergeMap(data =>
Rx.Observable.from([
Rx.Observable.of(data).mergeMap(data => saveClock(data.id, data.clock)),
Rx.Observable.timer(1000),
Rx.Observable.of(data).mergeMap(data => saveConfig(data.id, data.config))
])
.concatAll()
.toArray()
.map(() => ({ type: COMPLETED, id: data.id }))
);
Ben Lesh在他最近的AngularConnect演讲中描述了这种流的分离,在错误的背景下,它仍然适用:https://youtu.be/3LKMwkuK0ZE?t=20m(别担心,这不是' t Angular specific!)
接下来,我想分享一些不请自来的重构建议,这些建议可能会让您的生活更轻松,但当然这是自以为是的,所以可以随意忽略:
我会重构以更直观地反映事件的顺序,并降低复杂性:
const saveEpic = (action$, store) =>
action$.ofType(SAVE)
.mergeMap(data =>
Rx.Observable.from(saveClock(data.id, data.clock))
.delay(1000)
.mergeMap(() => saveConfig(data.id, data.config))
.map(() => ({ type: COMPLETED, id: data.id }))
);
我们在这里消耗saveClock
返回的Promise,将其输出延迟1000ms,mergeMapping结果调用saveConfig()
同时返回一个Promise被消耗然后最终将结果映射到我们的COMPLETE
操作。
最后,请记住,如果你的Epic 确实 保持活力并且长寿,那么这个史诗中没有任何东西阻止它接收多个SAVE请求而其他的请求仍在飞行中或尚未用尽请求之间所需的1000毫秒延迟。即如果确实需要任何请求之间的1000毫秒空间,那么你的史诗本身并不能完全阻止你的UI代码破坏它。在这种情况下,您可能需要考虑添加更复杂的缓冲backpressure机制,例如使用带有.zip()
的{{1}}运算符。
http://jsbin.com/waqipol/edit?js,output
BehaviorSubject
这使得在我们仍在处理现有请求时保存的请求被缓冲,并且我们一次只接受其中一个。请记住,这是一个无限制的缓冲区 - 这意味着待处理操作的队列可能比刷新缓冲区的速度无限快地增长。除非您采用有损背压策略,例如删除重叠请求等,否则这是不可避免的。
如果你有其他史诗有重叠的要求,不能每秒发送一次以上的请求,你需要创建某种单一的主管,为所有的史诗提供这种保证。
这可能看起来都非常复杂,但也许具有讽刺意味的是,在RxJS中,这比传统的命令式代码更容易实现 。最难的部分实际上是了解模式。