合并更改的Observable列表中的事件

时间:2018-01-15 19:55:23

标签: javascript events rxjs reactive-programming reactivex

我正在使用rxjs

我有一个Browser,它负责许多Page个对象。每个页面都有一个Observable<Event>,可以产生一系列事件。

Page个对象在不同时间关闭并打开。我想创建一个名为TheOneObservable的observable,它将合并所有当前活动的Page对象中的所有事件,并合并来自Browser对象本身的自定义事件。

关闭Page意味着应该关闭对它的订阅,这样就不会阻止它进行GC。

我的问题是Pages可以随时关闭,这意味着合并的Observable的数量总是在变化。我曾考虑使用Pages的Observable并使用mergeMap,但这有问题。例如,订阅者只会收到订阅后打开的Pages事件。

请注意,.NET已对here个问题进行了回答,但使用了ObservableCollection中未提供的rxjs

以下是一些用于说明问题的代码:

class Page {
    private _events = new Subject<Event>();

    get events(): Observable<Event> {
        return this._events.asObservable();
    }
}

class Browser {
    pages = [] as Page[];
    private _ownEvents = new Subject<Event>();

    addPage(page : Page) {
        this.pages.push(page);
    }

    removePage(page : Page) {
        let ixPage = this.pages.indexOf(page);
        if (ixPage < 0) return;
        this.pages.splice(ixPage, 1);
    }

    get oneObservable() {
        //this won't work for aforementioned reasons
        return Observable.from(this.pages).mergeMap(x => x.events).merge(this._ownEvents);
    }
}

它在TypeScript中,但它应该是可以理解的。

2 个答案:

答案 0 :(得分:2)

您可以switchMap()链接到数组的Subject()更改,在数组更改时将oneObservable替换为新的。

pagesChanged = new Rx.Subject();

addPage(page : Page) {
  this.pages.push(page);
  this.pagesChanged.next();
}

removePage(page : Page) {
  let ixPage = this.pages.indexOf(page);
  if (ixPage < 0) return;
  this.pages.splice(ixPage, 1);
  this.pagesChanged.next();
}

get oneObservable() {
  return pagesChanged
    .switchMap(changeEvent =>
      Observable.from(this.pages).mergeMap(x => x.events).merge(this._ownEvents)
    )
}

测试,

const page1 = { events: Rx.Observable.of('page1Event') }
const page2 = { events: Rx.Observable.of('page2Event') }

let pages = []; 
const pagesChanged = new Rx.Subject();
const addPage = (page) => { 
  pages.push(page); 
  pagesChanged.next(); 
}
const removePage = (page) => { 
  let ixPage = pages.indexOf(page);
  if (ixPage < 0) return;
  pages.splice(ixPage, 1);
  pagesChanged.next(); 
}

const _ownEvents = Rx.Observable.of('ownEvent')

const oneObservable = 
  pagesChanged
    .switchMap(pp => 
      Rx.Observable.from(pages)
        .mergeMap(x => x.events)
        .merge(_ownEvents)
    )

oneObservable.subscribe(x => console.log('subscribe', x))

console.log('adding 1')
addPage(page1)
console.log('adding 2')
addPage(page2)
console.log('removing 1')
removePage(page1)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>

答案 1 :(得分:0)

您需要自己管理页面订阅,并自行将其事件提供给结果主题:

const theOneObservable$ = new Subject<Event>();

function openPage(page: Page): Subscription {
  return page.events$.subscribe(val => this.theOneObservable$.next(val));
}

关闭页面,即在返回的订阅上调用unsubscribe,已经完成了它必须做的所有事情。

  

请注意theOneObservable$是一个热门观察点。

当然,您可以通过编写自己的可观察类型来更进一步,该类型封装了所有这些API。特别是,这将允许您在关闭时取消订阅所有内部可观察量。

这种方法略有不同:

const observables$ = new Subject<Observable<Event>>();
const theOneObservable$ = observables$.mergeMap(obs$ => obs$);

// Add a page's events; note that takeUntil takes care of the
// unsubscription process here.
observables$.next(page.events$.takeUntil(page.closed$));

这种方法的优越之处在于,当观察者被取消订阅时,它将自动取消订阅内部可观察量。