如果我有一个Rx.Observable,我如何通过forEach订阅多个函数?下面的代码有效,但这部分对我来说特别不干净:
Rx.Observable.from(definition).forEach(highlight);
Rx.Observable.from(definition).forEach(prefix);
我知道我可以创建一个包装函数来调用它们中的这两个,但我喜欢将它们分开的可读性.我喜欢的是做类似的事情
Rx.Observable.from(definition).forEach(highlight, prefix)
要么
definition = Rx.Observable.from(definition)
definition.forEach(highlight)
definition.forEach(prefix)
要么
Rx.Observable.from(definition).forEach(highlight).forEach(prefix)
……但这些都没有奏效.重构这个的最佳方法是什么?
JS:
(function commands() {
function highlight(node) {
node.classList.add(hoverClass);
}
function unhighlight(node) {
node.classList.remove(hoverClass);
}
function prefix(node) {
node.classList.add(prefixClass);
}
function unprefix(node) {
node.classList.remove(prefixClass);
}
function unprefixAll(nodes) {
Rx.Observable.from(nodes).forEach(unprefix);
}
var hoverClass = "hover";
var prefixClass = "prefixed";
var $commands = document.querySelector("#commands");
var definitions = Rx.Observable.from($commands.querySelectorAll("dt"))
.map(function(_, i) {
return $commands.querySelectorAll(
"dt:nth-of-type("+ (i + 1) +"), dt:nth-of-type("+ (i + 1) +") + dd"
);
});
definitions.forEach(function (definition) {
Rx.Observable.fromEvent(definition, "mouseover").forEach(function() {
definitions.forEach(unprefixAll);
Rx.Observable.from(definition).forEach(highlight);
Rx.Observable.from(definition).forEach(prefix);
});
Rx.Observable.fromEvent(definition, "mouseout").forEach(function() {
Rx.Observable.from(definition).forEach(unhighlight);
});
});
})();
HTML:
<dl id="commands">
<dt class="prefixed">command 1</dt>
<dd>does a thing for command 1</dd>
<dt>command 2</dt>
<dd>does a thing for command 2</dd>
<dt>command 3</dt>
<dd>does a thing for command 3</dd>
<dt>command 4</dt>
<dd>does a thing for command 4</dd>
<dt>help</dt>
<dd>Shows all available commands</dd>
</dl>
解决方法:
你应该记住,RxJS的一个真正的力量在于运算符组合.
而不是尝试通过所有东西(你可以使用传统的javascript数组而不需要包含Rx)来执行forEach()/ subscribe(),你应该尝试考虑事件在沿着管道传输时如何被转换和操纵.
以下是如何通过单个管道执行此操作的一个示例:
//Gets a subscription which can be used to clean up all the internal streams
//Use flatMap to flatten the inner streams into a single stream
var subscription = definitions.flatMap(function (d) {
var mouseOver = Rx.Observable.fromEvent(d, "mouseover");
var mouseOut = Rx.Observable.fromEvent(d, "mouseout");
var definition = Rx.Observable.from(d);
//Merge together both mouseOver and mouseOut so we can cancel them together later
//Use tap to apply side effects.
return Rx.Observable.merge(mouseOver.flatMap(definition)
.tap(prefix)
.tap(highlight),
mouseOut.flatMap(definition)
.tap(unprefix)
.tap(unhighlight));
}).subscribe();
编辑1
详细说明这里发生了什么:
>使用flatMap来监听定义流,每个值d将是一个匹配的元素来操纵.
>然后,我们为两个事件创建侦听器,并且一个迭代匹配的元素.
>接下来,我们再次使用flatMap来捕获每个事件(mouseOver和mouseOut),然后为我们匹配的元素投射observable.
>接下来,我们通过使用水龙头来应用副作用.
>将这两个流合并为一个流,这主要是为了将订阅传递回*.
>最后订阅整个链,然后当你处理返回的Disposable时,它也将清理所有内部流.
这是完整的工作示例:
(function commands() {
function highlight(node) {
node.classList.add(hoverClass);
}
function unhighlight(node) {
node.classList.remove(hoverClass);
}
function prefix(node) {
node.classList.add(prefixClass);
}
function unprefix(node) {
node.classList.remove(prefixClass);
}
var hoverClass = "hover";
var prefixClass = "prefixed";
var $commands = document.querySelector("#commands");
var definitions = Rx.Observable.from($commands.querySelectorAll("dt"))
.map(function(_, i) {
return $commands.querySelectorAll(
"dt:nth-of-type("+ (i + 1) +"), dt:nth-of-type("+ (i + 1) +") + dd"
);
});
var subscription = definitions.flatMap(function(d) {
//Declare this stuff up front
var mouseOver = Rx.Observable.fromEvent(d, "mouseover");
var mouseOut = Rx.Observable.fromEvent(d, "mouseout");
var definition = Rx.Observable.from(d);
return Rx.Observable.merge(mouseOver.flatMap(definition).tap(prefix).tap(highlight),
mouseOut.flatMap(definition).tap(unprefix).tap(unhighlight)).ignoreElements();
}).subscribe();
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script>
<dl id="commands">
<dt class="prefixed">command 1</dt>
<dd>does a thing for command 1</dd>
<dt>command 2</dt>
<dd>does a thing for command 2</dd>
<dt>command 3</dt>
<dd>does a thing for command 3</dd>
<dt>command 4</dt>
<dd>does a thing for command 4</dd>
<dt>help</dt>
<dd>Shows all available commands</dd>
</dl>