javascript – 如何在Rx.Observabale上链接订阅? (重构)

如果我有一个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>
上一篇:c# – Reactive Extensions SelectMany和Concat


下一篇:javascript – Subject vs AnonymousSubject