当另一个Observable发出时,操作员跳过源的下一个发射

时间:2017-07-03 14:15:13

标签: rxjs reactive-programming rxjs5

我有一个用例,当需要另一个通知程序Observable发出时,我需要一个Observable跳过它的下一个发射。

source:    |---X---X---X---X---X---X---X---X---X---X--|>
notifier:  |-------------X---------X----------X-------|>
result:    |---X---X---X-------X---X-------X-------X--|>

基本上,我想要一个名为skipNextWhen的运算符,它接收通知符observable并跳过源的下一个发射。

我尝试使用使用pausable运算符的实现(使用switchMap重新实现),但无法使其工作。

pausable.ts

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/observable/never';
import 'rxjs/add/operator/startWith';

declare module 'rxjs/Observable' {
    interface Observable<T> {
        pausable: typeof pausable;
    }
}

function pausable<T>(notifier: Observable<boolean>): Observable<T> {
    return notifier.startWith(false).switchMap((paused) => {
        if (paused) {
            return Observable.never();
        } else {
            const source = new Subject();
            this.subscribe(source);
            return source;
        }
    });
}

Observable.prototype.pausable = pausable;

skipNextWhen.ts

import { Observable } from 'rxjs/Observable';
import './pausable';

declare module 'rxjs/Observable' {
    interface Observable<T> {
        skipNextWhen: typeof skipNextWhen;
    }
}

function skipNextWhen<T, R>(other: Observable<T>): Observable<R> {
    const notifier = Observable.merge(this.map(() => false), 
                                      other.map(() => true));
    return this.pausable(notifier);
}

Observable.prototype.skipNextWhen = skipNextWhen;

我应该考虑使用更合适的操作员吗?我在当前实现中看到的行为是结果Observable发出一次,然后再也没有 - 即使通知程序Observable永远不会发出。

2 个答案:

答案 0 :(得分:4)

我可以想到两个解决方案:

  1. 使用.filter().do()和一些副作用。

    这可能更容易理解解决方案,即使它不是“Rx”方式:

    function skipNextWhen(other) {
        let skipNext = false;
    
        return this.merge(other.do(() => skipNext = true).filter(() => false))
            .filter(val => {
                const doSkip = skipNext;
                skipNext = false;
                return !doSkip;
            });
    }
    

    我正在使用merge()更新skipNextother的值始终被忽略。

  2. 使用.scan()

    此解决方案没有任何状态变量和副作用。

    function skipNextWhen(other) {
        const SKIP = 'skip';
    
        return this.merge(other.mapTo(SKIP))
            .scan((acc, val) => {
                if (acc === SKIP) {
                    return null;
                } else if (val === SKIP) {
                    return SKIP;
                } else {
                    return val;
                }
            }, [])
            .filter(val => Boolean(val) && val !== SKIP);
    }
    

    基本上,当SKIP到达时,我会立即将其返回,因为它会在acc运算符的scan()参数中再次传递,然后被filter()忽略。< / p>

    如果我收到正常值,但之前的值为SKIP,我会忽略它,只返回null,后来会过滤掉。

  3. 两种解决方案都给出了相同的结果:

    Observable.prototype.skipNextWhen = skipNextWhen;
    
    const source = Observable.range(1, 10)
        .concatMap(val => Observable.of(val).delay(100));
    
    source
        .skipNextWhen(Observable.interval(350))
        .subscribe(console.log);
    

    这将打印以下内容:

    1
    2
    3
    5
    6
    8
    9
    10
    

    请注意,您实际上并没有创建新的运营商。您只需要一个操作员链的快捷方式。例如,当源完成时,您无法取消订阅other

答案 1 :(得分:1)

我已经启动了一个(非常)小的我想要的rxjs utils库。它恰好有一个功能可以完全满足您的要求:skipAfter。从文档中:

source: -1-----2-----3-----4-----5-|
skip$:  ----0----------0-0----------

result: -1-----------3-----------5-|

图书馆在这里:https://github.com/simontonsoftware/s-rxjs-utils