Rx:如何缓冲项目直到满足条件

时间:2016-02-10 08:07:41

标签: rx-java reactive-programming

我有一个项目流,我想缓冲它们,直到其中一个项目符合条件。一旦满足条件,缓冲的项目应该交付给订户。

如果源可观察完成但尚未满足条件,我希望产生错误。

这可以使用RxJava中的默认运算符或Rx系列的其他语言实现吗?

1 个答案:

答案 0 :(得分:1)

我已经使用RxJS编写了下面的示例,以便我们可以在此处运行示例。这些概念与RxJava相同。

A BufferedSubject

class BufferedSubject extends Rx.Subject {
    constructor(predicate) {
        super();
        this.predicate = predicate;
        this.buffer = [];
        this.unbuffered = false;
    }

    onNext(e) {
        if (this.unbuffered) {
            super.onNext(e);
            return;
        }

        if (this.predicate(e)) {
            while (this.buffer.length) {
                super.onNext(this.buffer.shift());
            }

            super.onNext(e);
            this.unbuffered = true;
            return;
        }

        this.buffer.push(e);
    }
}

您可以像使用常规主题一样使用BufferedSubject,作为某个源可观察源与您期望的缓冲可观察量之间的中介 - 您订阅主题并且主题订阅源可观察源,主题维护项目的缓冲区,直到某些条件成立。例如:

const timer = Rx.Observable.interval(5000);
const subject = new BufferedSubject((item) => item > 5);
subject.subscribe(
    x => $('.list').append(`<li>${x} seconds</li>`)
);
timer.subscribe(subject);

作为一个可运行的片段(从ES6编译,抱歉):

&#13;
&#13;
'use strict';
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError('Cannot call a class as a function');
    }
}
function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');
    }
    return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}
function _inherits(subClass, superClass) {
    if (typeof superClass !== 'function' && superClass !== null) {
        throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass)
        Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var BufferedSubject = function (_Rx$Subject) {
    _inherits(BufferedSubject, _Rx$Subject);
    function BufferedSubject(predicate) {
        _classCallCheck(this, BufferedSubject);
        var _this = _possibleConstructorReturn(this, _Rx$Subject.call(this));
        _this.predicate = predicate;
        _this.buffer = [];
        _this.unbuffered = false;
        return _this;
    }
    BufferedSubject.prototype.onNext = function onNext(e) {
        if (this.unbuffered) {
            _Rx$Subject.prototype.onNext.call(this, e);
            return;
        }
        if (this.predicate(e)) {
            while (this.buffer.length) {
                _Rx$Subject.prototype.onNext.call(this, this.buffer.shift());
            }
            _Rx$Subject.prototype.onNext.call(this, e);
            this.unbuffered = true;
            return;
        }
        this.buffer.push(e);
    };
    return BufferedSubject;
}(Rx.Subject);
var timer = Rx.Observable.interval(1000).skip(1).take(50).publish();
var subject = new BufferedSubject(function (item) {
    return item > 5;
});
subject.subscribe(function (x) {
    return $('.list').append('<li>' + x + ' seconds</li>');
}, function (e) {
    return $('.list').append('<li>Errror ' + e + '</li>');
}, function (_) {
    return $('.list').append('<li>Sequence complete</li>');
});
timer.subscribe(subject);
timer.subscribe(function (x) {
    return $('.timer').text(x + ' seconds elapsed');
});
timer.connect();
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<p>Output (<span class='timer'>-</span>):</p>

<ul class="list">
</ul>
&#13;
&#13;
&#13;

(也可在CodePen上找到。)

注意:我说这个实现不能正确支持背压。