无法实现BufferUntil RxJava

时间:2017-02-16 11:07:11

标签: java rx-java

尝试为BufferUntil 实现Observable(即缓冲元素,并在满足某些条件时发出缓冲列表)

为RxNET实施

定义:

public static IObservable<IList<TSource>> BufferUntil<TSource>(
              this IObservable<TSource> source, 
              Func<TSource, bool> predicate)
    {
        var published = source.Publish().RefCount();
        return published.Buffer(() => published.Where(predicate));
    }

使用:

var list = new List<char> { 'a', 'b', 'c', 'd', 'e', 'c' };
list
    .ToObservable()
    .BufferUntil(c => c == 'c')
    .Subscribe(c => c.ToList().ForEach(Console.WriteLine));

结果:

工作正常。发出2 IList char ['a','b','c'] ['d','e','c']public <T extends Object> Observable<List<T>> bufferUntil( Observable<T> source, final Func1<T, Boolean> bufferClosingCriteria) { final Observable<T> published = source.publish().refCount(); return published.buffer(new Func0<Observable<T>>() { @Override public Observable<T> call() { return published.filter(bufferClosingCriteria); } }); }

尝试为RxJava实现完全相同的

定义:

Character[] arr = {'a','b','c','d','e','c'};

bufferUntil(rx.Observable.from(arr), new Func1<Character, Boolean>() {
    @Override
    public Boolean call(Character character) {
        return character == 'c';
    }
}).subscribe(new Action1<List<Character>>() {
    @Override
    public void call(List<Character> o) {
        for (int i = 0; i < o.size(); ++i)
           Log.d("LL", o.get(i).toString());
    }
});

使用:

    SELECT
        renewaldate,
        DATEDIFF(renewaldate, CURDATE()) AS DaysUntilRenewalDate,
        orders.*, customers.*
    FROM
        orders
    JOIN customers ON orders.CustomerNumber = customers.CustomerNumber
    WHERE 
    RenewalDate < DATE_ADD(CURDATE(), INTERVAL 23 DAY)
    AND RenewalDate > DATE_SUB(CURDATE(), INTERVAL 4 DAY)
    AND IF MakeCall IS NOT NULL THEN WHERE ITS = TO TODAY?
    ORDER BY
        RenewalDate

结果:

它会发出 3空列表

RxJava实现有什么问题和/或如何解决?

1 个答案:

答案 0 :(得分:2)

问题在于,由于默认情况下是同步的,RxJava的工作方式不同,而Rx.NET具有异步源。

RxJava中发生的情况是,当边界订阅refCount时,上游开始立即发出所有字符,遍历字符数组,边界在看到&#34; c&#34;时保持信号。此时,缓冲区操作员甚至没有订阅相同的refCount源,也从未看到任何来自它的数据。

在Rx.NET中,据我所知,List.ToObservable进入另一个线程来发出数组项,这为原始线程提供了足够的时间让缓冲区及其边界得到订阅RefCount。

如果您添加了一个小延迟source.delay(500, TimeUnit.MILLISECONDS).publish().refCount(),您会看到数据和列表通过,但列表内容仍然不同:

[a, b]
[c, d, e]
[c]

原因是在RxJava中,边界Observable首先被订阅,然后是缓冲订阅者,因此当&#34; c&#34;它来到,它首先到达边界,触发缓冲区分裂,然后缓冲区运算符获得值&#34; c&#34;并将其存储在新缓冲区中。

要确保操作员使用同步源,您必须使用publish(Func1)而不是refCount:

public static <T extends Object> Observable<List<T>> bufferUntil(
        Observable<T> source,
        final Func1<T, Boolean> bufferClosingCriteria) {

    return source.publish(o -> o.buffer(() -> o.filter(bufferClosingCriteria)));
}

publish()变体确保了每个潜在的消费者&#34; o&#34;在消耗源之前设置。

重新订阅订阅以确保&#34; c&#34;最终在缓冲区的末尾有一点涉及:

return source.publish(o -> {
    PublishSubject<Object> ps = PublishSubject.create();
    return o.buffer(() -> ps)
            .mergeWith(Observable.defer(() -> {
                o.filter(bufferClosingCriteria).subscribe(ps);
                return Observable.empty();
            }))
            .filter(list -> !list.isEmpty());
});

收率:

[a, b, c]
[d, e, c]