如果我有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>
答案 0 :(得分:5)
你应该记住,RxJS的一个真正的力量在于算子构成。
不是试图forEach()/subscribe()
通过所有内容(你可以使用传统的javascript数组而不需要包含Rx),而是应该考虑事件在沿着管道传输时如何被转换和操纵。 / p>
以下只是一个如何通过单一管道实现的例子:
//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
收听definitions
信息流,每个值d
都是一个匹配的操作元素。flatMap
来捕获每个事件(mouseOver和mouseOut),并为我们的匹配元素项目提供observable。tap
。Merge
将这两个流合并为一个流,这主要是为了将订阅传递回顶层。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();
})();
&#13;
<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>
&#13;