combineAll 不会在空数组上发射

combineAll does not emit on empty array

JSBIN Sample

我有一组可变的子组件(POJO 对象),每个子组件都有自己的状态流。每次用户触发 addChild/removeChild/clearChildren 时,都会使用#switchMap 发出一组新的子状态流。到目前为止,一切都很好! (对 RxJS 感到非常惊讶!)

使用 Rx.Observable.from(arrayOfStateStreams).combineAll() 只要 arrayOfStateStreams 不是空数组,我就会得到很好的结果。

由于这是在更高级别上组合(最新)的部分状态,因此我需要发出一个空数组,否则全局状态树将包含不再正确的旧状态数据!

我可以发出一些保留令牌,例如 ['EMPTY-ARRAY-PLACEHOLDER-TOKEN'],但这很奇怪。 更好的方法是始终将最后一个流附加到数组中,这样最后一个索引就可以被视为垃圾。虽然仍然混淆代码和状态。 使用 [null] 是不行的,因为我们可以有一个 'null'.

的子状态

谁能解决这个问题?这不能支持,因为在#combineAll 之后不应该有空数组的其他表示吗?

这与combineAll无关。问题是当传递一个空数组时 Observable.from 没有任何结果(一个空的可观察对象)。

如果必须从空数组中获取结果,我能想到的唯一可行解决方案是 return 在这种情况下使用其他方法。

举例说明问题和可能的解决方案。

var data = [1, 2, 3, 4, 5];

log('With data: ');
Rx.Observable.from(data)
    .subscribe(function (d) { log('data: ' + d); });

// Prints: 
// With data: 
// data: 1
// data: 2
// data: 3
// data: 4
// data: 5

var data = [];

log('Without data: ');
var nullDataObject = { msg: 'my null data object' };
Rx.Observable.from(data.length == 0 ? [nullDataObject] : data)
    .subscribe(function (d) { log('data: ' + d); });

// Prints: 
// With data: 
// data: [object Object]

jsfiddle 上的可运​​行示例。

使用它时,您只需在适当的地方过滤掉代表空数组的对象。

致谢名单 github user trxcllnt who provided the following answer:

combineAll won't emit unless the combined Observables emit at least one value, but you could check to ensure the collection you're combining is empty or not, and either combine or emit an empty Array:

 var arrayOfStreamsStream = Rx.Observable
    .of(
        [], [
            Rx.Observable.of('blah-1'), // component state.
            Rx.Observable.of('blah-2'),
            Rx.Observable.of('blah-3')
        ], [], [
            Rx.Observable.of('foo-1'),
            Rx.Observable.of('qux-2')
        ]
    )
    .switchMap(function onMap(coll) {
        return coll.length === 0 ?
            Observable.of(coll) :
            Observable.combineLatest(...coll);
    })
    .subscribe(function onSubscribe(data) {
        console.log('onSubscribe START')
        console.dir(data)
        console.log('onSubscribe END')
    }) 

一个可能的解决方法是使用 startWith();

combineLatest(potentiallyEmptyArray).pipe(
    startWith<any>([])
);

注意:combineLatest()(静态版本)也存在类似的问题,可以使用 defaultIfEmpty() 来解决 - 这有效,但它会搞砸输出的输入。

// array of Observables
const animals: Observable<{ species: 'dog' | 'cat' }>[] = [];  

// Type '{ species: "dog" | "cat"; }[]' is not assignable to type 'never[]'.
combineLatest(animals).pipe(defaultIfEmpty([]));  

在 TypeScript 中,您需要知道对象的类型或使用 <any>[],这意味着您会完全失去输入。

如果您有具体类型,您可以使用其中之一:

defaultIfEmpty<Animal[]>([])

defaultIfEmpty([] as Animal[])

我通常没有可观察值的 return 值的具体类型。所以我想出了一个运算符:

 export const emptyArrayIfEmpty = () => <T>(observable: Observable<T[]>) =>
                                        observable.pipe(defaultIfEmpty([] as T[]));

然后我可以添加以下内容并在 animals === [] 不丢失任何输入信息的情况下得到一个空数组:

combineLatest(animals).pipe(emptyArrayIfEmpty());