如何将一个RXJs流用于两个不同的事物

时间:2016-02-12 16:49:16

标签: javascript typescript rxjs

我的应用程序中有2个服务。一个youTube服务,通过线路加载youTube注释线程和注释,以及管理批量注释加载的注释服务。评论服务特定于我的应用程序,youTube服务与应用程序无关。

我有一个加载注释线程的函数getCommentThreadsForChannel。这方面的主要实现是在youTube服务中,但是在评论服务上调用它,基本上只是调用从youTube服务返回observable。

就我的调用此函数的控制器而言,这只是一个可观察的注释线程序列。但是,在我的commentService中,我想在本地存储这些线程。每当我得到100多个线程时,我想批量存储所有线程,这样我就不会为每一个新的数据位处理列表。我想出了这段代码:

    getCommentThreadsForChannel(): Rx.Observable<ICommentThread> {
        var threadStream: Rx.Observable<ICommentThread> =
            this.youTubeService.getCommentThreadsForChannel();

        threadStream
            .bufferWithCount(100)
            .scan( ( allItems, currentItem ) => {
                currentItem.forEach(thread => {
                    allItems.push(thread);
                });

                console.log( `Save items to local storage: ${allItems.length}` )

                return allItems;
            }, []  );

        return threadStream;
    }

我认为这里用于批处理线程并将所有线程累积到一个数组中的逻辑很好但是从不调用此代码。我认为这是因为我根本没有订阅这个帖子。

我不想在这里订阅,因为它会订阅底层流,然后我会有2个订阅,所有数据都会加载两次(有很多数据 - 大约需要一分钟才能加载它们并且每次超过30个100个线程的调用。)

我基本上想要一个do,这不会影响传递给控制器​​的流,但我想使用RXjs的缓冲和累积逻辑。

我认为我需要以某种方式共享或发布流,但我之前使用这些运算符几乎没有成功,并且无法在不添加第二个订阅的情况下看到我能够做到这一点。

如何共享一个流并以两种不同的方式使用它而无需订阅两次?我可以创建某种被动流,只有在基于订阅的可观察对象时才进行订阅吗?

2 个答案:

答案 0 :(得分:1)

我最终想到了这一点(在@MonkeyMagiic的帮助下)。

我必须共享流,以便我可以对数据执行2个不同的操作,并将它们分别缓存在每个值上。问题是这两个流都必须订阅,但我不想订阅服务 - 这应该在控制器中完成。

解决方案是再次组合2个流并忽略缓冲区中的值:

var intervalStream = Rx.Observable.interval(250)
    .take(8)
    .do( function(value){console.log( "source: " + value );} )
    .shareReplay(1);

var bufferStream = intervalStream.bufferWithCount(3)
    .do( function(values){
      console.log( "do something with buffered values: " + values );
    } )
    .flatMap( function(values){ return Rx.Observable.empty(); } );

var mergeStream = intervalStream.merge( bufferStream );

mergeStream.subscribe(
  function( value ){ console.log( "value received by controller: " + value ); },
  function( error ){ console.log( "error: " + error ); },
  function(){ console.log( "onComplete" ); }
);

输出:

"source: 0"
"value received by controller: 0"
"source: 1"
"value received by controller: 1"
"source: 2"
"value received by controller: 2"
"do something with buffered values: 0,1,2"
"source: 3"
"value received by controller: 3"
"source: 4"
"value received by controller: 4"
"source: 5"
"value received by controller: 5"
"do something with buffered values: 3,4,5"
"source: 6"
"value received by controller: 6"
"source: 7"
"value received by controller: 7"
"do something with buffered values: 6,7"
"onComplete"

JSBin

答案 1 :(得分:0)

我相信你要找的是sharereplay的组合,幸好RX有shareReplay(bufferSize)

var threadStream: Rx.Observable<ICommentThread> = this.youTubeService
                .getCommentThreadsForChannel()
                .shareReplay(1); 

getCommentThreadsForChannel(): Rx.Observable<ICommentThread> {
        threadStream
            .bufferWithCount(100)
            .scan( ( allItems, currentItem ) => {
                currentItem.forEach(thread => {
                    allItems.push(thread);
                });

                console.log( `Save items to local storage: ${allItems.length}` )

                return allItems;
            }, []  );

        return threadStream;
    }

使用多播运营商共享将确保在第一个之后的每个附加订阅都不会发出不必要的请求,并且重播是为了确保所有将来的订阅都收到最后的通知。