我有一个Observable<Array<Observable<T>>>
,我想映射到Observable<Array<T>>
。
发射新数组时,内部可观察对象应按以下方式取消订阅/订阅:
Observable
存在于先前的数组和新的/当前的数组中,则保留预先存在的订阅Observable
在先前的数组中不存在,但在新的/当前的数组中存在,则创建新的订阅 Observable
在先前的数组中存在,但在新的/当前的数组中不存在,请从预先存在的订阅中取消订阅 我希望在外部可观察的物体上使用switchMap
,然后将Array<Observable<T>>
传递到combineLatest
中来实现此目的。但是,switchMap
将在订阅新的内部Observable
之前从其先前的内部Observable
退订,这意味着内部订阅不会按需保留。
示例(https://stackblitz.com/edit/typescript-b4wgr1)。给定代码:
import 'rxjs/Rx';
import { Observable } from 'rxjs';
const debugObservable = <T>(t$: Observable<T>, name: string) =>
new Observable<T>(observer => {
console.log(name, 'subscribe');
const subscription = t$.subscribe(observer);
return () => {
console.log(name, 'unsubscribe');
return subscription.unsubscribe();
};
});
const ofSingle = <T>(t: T) =>
new Observable<T>(observer => {
observer.next(t);
});
const observableOfArrayOfObservablesOfNumber = new Observable<
Array<Observable<number>>
>(observe => {
const keep = debugObservable(ofSingle(1), 'keep');
const remove = debugObservable(ofSingle(2), 'remove');
const add = debugObservable(ofSingle(3), 'add');
observe.next([keep, remove]);
setTimeout(() => {
observe.next([keep, add]);
}, 2000);
return () => {};
});
// The `switchMap` will unsubscribe to the previous inner observable *before* subscribing to the new
// inner observable.
const final$ = observableOfArrayOfObservablesOfNumber.switchMap(
arrayOfObservablesOfNumber => {
const observableOfArrayOfNumbers = Observable.combineLatest(
arrayOfObservablesOfNumber,
);
return debugObservable(
observableOfArrayOfNumbers,
'observableOfArrayOfNumbers',
);
},
);
final$.subscribe(x => console.log('final', x));
这将产生:
observableOfArrayOfNumbers subscribe
keep subscribe
remove subscribe
final [1, 2]
keep unsubscribe <--- bad!
remove unsubscribe
observableOfArrayOfNumbers unsubscribe
observableOfArrayOfNumbers subscribe
keep subscribe <--- bad!
add subscribe
final [1, 3]
但是,这就是我想要的:
observableOfArrayOfNumbers subscribe
keep subscribe
remove subscribe
final [1, 2]
remove unsubscribe
observableOfArrayOfNumbers unsubscribe
observableOfArrayOfNumbers subscribe
add subscribe
final [1, 3]
答案 0 :(得分:0)
我最终通过使用publishReplay(1)
发布+重播内部可观察对象,然后进行引用计数来实现这一目标。
请注意,refCount
是不够的,因为当0
取消订阅先前的内部可观察对象(在订阅新的内部可观察对象之前)时,计数将下降到switchMap
。使用特殊的refCountWithDelay
运算符,该运算符仅在延迟后(即,在事件循环的同一滴答声中但不同步)通过引用计数来取消订阅。此处的更多信息:
refCountWithDelay
:https://gist.github.com/marinho/3637210b13c0f298e1692a0b7b104e64 https://stackblitz.com/edit/typescript-4xfwsh?file=index.ts
const createObservable = <T>(t: T, name: string) => {
return refCountWithDelay(debugObservable(ofSingle(t), name).publishReplay(1), 0, 0);
}
const observableOfArrayOfObservablesOfNumber = new Observable<
Array<Observable<number>>
>(observe => {
const keep = createObservable(1, 'keep');
const remove = createObservable(2, 'remove');
const add = createObservable(3, 'add');
observe.next([keep, remove]);
setTimeout(() => {
observe.next([keep, add]);
}, 2000);
return () => {};
});
产生:
observableOfArrayOfNumbers subscribe
keep subscribe
remove subscribe
final [1, 2]
observableOfArrayOfNumbers unsubscribe
observableOfArrayOfNumbers subscribe
remove unsubscribe
add subscribe
final [1, 3]
请注意,keep
只订阅了一次。
答案 1 :(得分:0)
我想出了一个更好的解决方案,可以使用None is (None == None)
(None is None) == None
中的combineLatestHigherOrder
:https://github.com/cartant/rxjs-etc
答案 2 :(得分:-1)
与您所描述的最接近的是在Cycle.js Onionify中称为pickCombine的xstream运算符。
似乎没有一个单一的官方RxJS运算符可以解决此问题,但是可以构建自己的运算符来实现此行为。您可以使用pickCombine的xstream实现作为参考。
关键部分是:
请注意,创建custom data structure(使用Map并依靠键来消除数组项的歧义)比直接在数组上进行创建要容易和高效。您可以从外部API隐藏自定义数据结构。