RxJS:自动(dis)连接(un)订阅Websockets和Stomp

时间:2017-03-04 23:15:35

标签: rxjs reactive-programming rxjs5 reactivex reactive

我正在构建一个用于Stomp over Websockets的小型RxJS Wrapper,它已经可以使用了。

但现在我想到了一个非常酷的功能,可能(希望 - 如果我错了,请纠正我)使用RxJS轻松完成。

当前行为:

myStompWrapper.configure("/stomp_endpoint");
myStompWrapper.connect();      // onSuccess: set state to CONNECTED

// state (Observable) can be DISCONNECTED or CONNECTED
var subscription = myStompWrapper.getState()
    .filter(state => state == "CONNECTED")
    .flatMap(myStompWrapper.subscribeDestination("/foo"))
    .subscribe(msg => console.log(msg));

// ... and some time later:
subscription.unsubscribe();    // calls 'unsubscribe' for this stomp destination
myStompWrapper.disconnect();   // disconnects the stomp websocket connection

如您所见,必须等待state == "CONNECTED"才能订阅subscribeDestination(..)。否则我会从Stomp库中得到错误。

新行为:

下一个实现应该让用户更容易。这就是我的想象:

myStompWrapper.configure("/stomp_endpoint");

var subscription = myStompWrapper.subscribeDestination("/foo")
    .subscribe(msg => console.log(msg));

// ... and some time later:
subscription.unsubscribe();

内部如何运作

  1. configure只能在DISCONNECTED
  2. 时调用
  3. 调用subscribeDestination时,有两种可能性:
    1. 如果CONNECTED:只是订阅目的地
    2. 如果DISCONNECTED:先致电connect(),然后订阅目的地
  4. 调用unsubscribe时,有两种可能性:
    1. 如果这是最后一次订阅:请致电disconnect()
    2. 如果这不是最后一次订阅:什么都不做
  5. 我还不确定如何到达那里,但这就是我在这里提出这个问题的原因;-)

    提前致谢!

    编辑:更多代码,示例和解释

    断开连接时调用 configure()时,它应该抛出Error。但这不是什么大问题。

    stompClient.connect(..)是非阻止的。它有一个onSuccess回调:

    public connect() {
      stompClient.connect({}, this.onSuccess, this.errorHandler);
    }
    
    public onSuccess = () => {
      this.state.next(State.CONNECTED);
    }
    

    observeDestination(..)订阅Stomp消息频道(=目的地)并返回Rx.Observable,然后可以用来取消订阅此Stomp消息频道:

    public observeDestination(destination: string) {
      return this.state
          .filter(state => state == State.CONNECTED)
          .flatMap(_ => Rx.Observable.create(observer => {
            let stompSubscription = this.client.subscribe(
                destination,
                message => observer.next(message),
                {}
            );
    
            return () => {
              stompSubscription.unsubscribe();
            }
          }));
    }
    

    可以这样使用:

    myStompWrapper.configure("/stomp_endpoint");
    myStompWrapper.connect();
    
    myStompWrapper.observeDestination("/foo")
        .subscribe(..);
    
    myStompWrapper.observeDestination("/bar")
        .subscribe(..);
    

    现在我想摆脱myStompWrapper.connect() 。代码应在第一个订阅时通过调用this.connect()自动调用observeDestination(..).subscribe(..),并在最后一个调用this.disconnect()时调用unsubscribe()

    示例:

    myStompWrapper.configure("/stomp_endpoint");
    
    let subscription1 = myStompWrapper.observeDestination("/foo")
        .subscribe(..); // execute connect(), because this
                        // is the first subscription
    
    let subscription2 = myStompWrapper.observeDestination("/bar")
        .subscribe(..);
    
    subscription2.unsubscribe();
    subscription1.unsubscribe(); // execute disconnect(), because this 
                                 // was the last subscription
    

1 个答案:

答案 0 :(得分:0)

RxJS: Auto (dis)connect on (un)subscribe with Websockets and Stomp

我同意你建议收藏到myStompWrapper的代码在新家里会更快乐。

我仍然建议使用observeDestination而不是subscribeDestination("/foo")之类的名称,因为您实际上并未订阅该方法,而只是完成您的可观察链。

  1. configure()只能在DISCONNECTED

    时调用

    如果在DISCONNECTED之间调用它时,您没有指定应该发生什么。由于您似乎没有返回任何您将使用的值,我将假设您打算在状态不方便时抛出异常。要跟踪此类状态,我会使用以BehaviourSubject的初始值开头的DISCONNECTED。您可能希望在observeDestination内保持状态以决定是否抛出异常

  2. 如果已连接:只需订阅目的地

    如果DISCONNECTED:首先调用connect(),然后订阅目标

    正如我之前提到的,如果订阅不在subscribeDestination("/foo")内发生,我认为你会更高兴,而只是建立你的可观察链。由于您只是想在某些情况下调用connect(),我只需在您的可观察链中使用.do()调用,该调用包含状态条件。

  3. 要使用rx-y逻辑,您可能希望将disconnect()作为可观察的取消订阅的一部分,并简单地返回一个共享的refcounted observable来开始。这样,每个新订阅者都不会重新创建新订阅,而.refCount()只会在下游没有订阅者的情况下对可观察链和unsubscribe()进行单一订阅。

  4. 假设消息是以myStompWrapper中的 this.observedData $ 进入的,myStompWrapper中我建议的代码看起来像这样:

    observeDestination() {
      return Rx.Observable.create(function (observer) {
         var subscription = this.getState()
                 .filter(state => state == "CONNECTED")
                 .do(state => state ? this.connect() : Observable.of(true))
                 .switchMap(this.observedData$)
                 .refCount();
                 .subscribe(value => {
                   try {
                     subscriber.next(someCallback(value));
                   } catch(err) {
                     subscriber.error(err);
                   }
                 },
                 err => subscriber.error(err),
                 () => subscriber.complete());
    
     return { unsubscribe() { this.disconnect(); subscription.unsubscribe(); } };
    }
    

    因为我遗漏了一些代码,所以我不允许自己测试代码。但希望它能说明并提出我在答案中提到的概念。