检索可观察的订户并使其订阅另一个可观察的订户

时间:2019-07-25 14:53:29

标签: rxjs rxjs-observables

简单地说

鉴于现有的Observable(尚未完成),有没有办法检索关联的订阅者(传递给订阅的函数)以使他们订阅另一个Observable?

上下文

我的应用程序中的服务有助于创建 SeverEvent 连接,将 ConnectableObservable 返回到代理连接,并允许使用 publish 运算符进行多播。该服务通过内部存储跟踪现有连接:

store: {[key: string]: ConnectionTracker};

// …

interface ConnectionTracker {
    url: string;
    eventSource: EventSource;
    observable: rx.ConnectableObservable<any>;
    subscription: rx.Subscription;
    observer: rx.Observer<any>;
    data?: any; // Arbitrary data
}

在创建连接时,如果已经存在关联的跟踪器(使用连接的端点进行标识),则服务应:

  • 确定关闭现有跟踪器的 ServerEvent 连接
  • 确定打开新的 SerevrEvent 连接(因此将创建新的ConnectableObservable)
  • 用新的可观察的替换现有跟踪器的“可观察的”,但现在让现有的订户订阅新的可观察的

这是创建 ConnectionTracker s

的代码部分
/**
* Create/Update a ServerEvent connection tracker
*/
createTracker<T>(endpoint: string, queryString: string = null): ConnectionTracker
{
    let fullUri = endpoint + (queryString ? `?${queryString}` : '')
        , tracker = this.findTrackerByEndpoint(endpoint) || {
            observable: null,
            fullUri: fullUri,
            eventSource: null,
            observer: null,
            subscription: null
        }
    ;

    // Tracker exists
    if (tracker.observable !== null) {
        // If fullUri hasn't changed, use the tracker as is
        if (tracker.fullUri === fullUri) {
            return tracker;
        }

        // At this point, we know "fullUri" has changed, the tracker's
        // connection should be replaced with a fresh one

// ⇒ TODO
// ⇒ Gather old tracker.observable's subscribers/subscriptions to make
//   them subscribe to the new Observable instead (created down below)

        // Terminate previous connection and clean related resouces
        tracker.observer.complete();
        tracker.eventSource.close();
    }

    tracker.eventSource = new EventSource(<any>fullUri, {withCredentials: true});
    tracker.observable = rx.Observable.create((observer: rx.Observer<T>) => {
            // Executed once
            tracker.eventSource.onmessage = e => observer.next(JSON.parse(e.data));
            tracker.eventSource.onerror = e => observer.error(e);
            // Keep track of the observer
            tracker.observer = observer;
        })
        // Transform Observable into a ConnectableObservable for multicast
        .publish()
    ;

    // Start emitting right away and also keep a reference to 
    // proxy subscription for later disposal
    tracker.subscription = tracker.observable.connect();

    return tracker;
}

谢谢。

2 个答案:

答案 0 :(得分:0)

我认为不可能,因此我将针对您的问题提出不同的解决方案。

与其尝试手动将订户从一个可观察对象转移到另一观察对象,不如为接收者提供一个可观察对象,该对象将在需要时自动切换到另一个可观察对象。您可以通过使用更高阶的Observables来实现。

为每个端点创建一个BehaviorSubject跟踪器供应商),它发出该端点当前应使用的Observable。 。当应为给定的端点使用不同的Observable( tracker )时,请将此新的Observable传递给BehaviorSubject tracker供应商 )。让您的接收者订阅BehaviorSubject跟踪器供应商),后者会自动向其提供正确的 tracker ,即切换到当前应使用的Observable。< / p>

您的代码的简化版本可能类似于以下代码。具体情况取决于您在整个应用中如何使用函数createTracker

interface ConnectionTracker {
  fullUri: string;
  tracker$: ConnectedObservable<any>;
}

// Map an endpoint to a tracker supplier.
// This is your higher order Observable as it emits objects that wrap an Observable
var store: { [key: string]: BehaviorSubject<ConnectionTracker> };

// Creates a new tracker if necessary and returns a ConnectedObservable for that tracker. 
// The ConnectedObservable will always resemble the current tracker.
createTracker<T>(endpoint: string, queryString: string = null): Observable<any> {
  const fullUri = endpoint + (queryString ? `?${queryString}` : '');
  // if no tracker supplier for the endpoint exists, create one
  if (!store[endpoint]) {
    store[endpoint] = new BehaviorSubject<ConnectionTracker>(null);
  }
  const currentTracker = store[endpoint].getValue();

  // if no tracker exists or the current one is obsolete, create a new one
  if (!currentTracker || currentTracker.fullUri !== fullUri) {
    const tracker$ = new Observable<T>(subscriber => {
      const source = new EventSource(fullUri, { withCredentials: true });
      source.onmessage = e => subscriber.next(JSON.parse(e.data));
      source.onerror = e => subscriber.error(e);
      return () => source.close();
    }).pipe(publish()) as ConnectableObservable<any>;
    tracker$.connect();
    // pass the new tracker to the tracker supplier
    store[endpoint].next({ fullUri, tracker$ });
  }
  // return the tracker supplier for the given endpoint that always switches to the current tracker
  // switchMap will unsubscribe from the previous tracker and thus close the connection if a new tracker comes in
  return store[endpoint].pipe(switchMap(tracker => tracker.tracker$));
}

答案 1 :(得分:0)

如果您尝试执行将订户移动到其他可观察对象之类的操作,那么您只是没有在RxJS中执行预期的操作。任何此类操纵基本上都是黑客行为。

如果您偶尔会产生一个新的可观察对象(例如,通过发出请求),并且您希望某些订阅者始终订阅其中的最新消息,那么这里就是解决方案:

  private observables: Subject<Observable<Data>> = new Subject();

  getData(): Observable<Data> {
    return this.observables.pipe(switchAll());
  }

  onMakingNewRequest(newObservable: Observable<Data>) {
    this.observables.push(newObservable);
  }

通过这种方式,您可以公开(通过getData())客户端订阅的单个可观察对象,但是通过推送到this.observables,可以更改用户看到的实际数据源。

关于关闭连接和类似的东西,您的可观察对象(随每个请求或某物创建的对象)基本上应该负责在取消订阅时释放和关闭它,然后您无需进行任何额外的处理,从您推送新的可观察对象起,该可观察对象将自动取消订阅。详细信息取决于您要联系的实际后端。