将未知数量的并列子任务合并为一个可完成

时间:2017-02-28 09:35:11

标签: java android rx-java reactive-programming

我需要创建并发网络请求。根据这些请求的结果,可能会启动更多请求。

我希望在所有请求完成后获得单个Completable,并且不需要创建进一步的请求。

我的问题是,是否可以使用以下代码段实现:

return Completable.defer(() -> {
        startRequests();
        return Observable.merge(requestSubject.asObservable()).toCompletable();
    });

在此示例中,startRequest会将网络请求(Retrofit)添加到requestSubject,即PublishSubject<Observable<SomeResponse>>

具体而言,我希望网络请求一旦订阅就在IO调度程序上启动,并且返回的Completable要完成,直到我在其中一个请求的onNext中调用requestSubject.onComplete()

我还没弄清楚如何在不执行请求两次的情况下处理请求的响应(每次订阅都要进行Retrofit请求)。

它是以这种方式工作,还是有更好的方法来实现我想要的东西?谢谢!

3 个答案:

答案 0 :(得分:2)

只需使用flatmap()并将其转换为Completable

这是一个(模拟)执行网络请求的示例,该请求返回io池上的2个项目,然后它对computation池中的这些项执行计算,并行:

@Test
public void foo() throws Exception {
    Observable.range(1, 10)
            .flatMap(this::getNItemsFromNetwork)
            .flatMap(this::asyncCompuatation)
            .ignoreElements()
            .subscribe(() -> System.out.println("onComplete"),
                    (t) -> System.out.println("onError"));

    Thread.sleep(10000);
}

Observable<String> getNItemsFromNetwork(int count) {
    return Observable.just(count)
            .subscribeOn(Schedulers.io())
            .doOnNext(i -> System.out.println("Executing request for " + count + " on thread: " + Thread.currentThread()))
            .flatMap(number -> Observable.just("Item nr " + number + ".1", "Item nr " + number + ".2"))
            .delay(random.nextInt(1000), TimeUnit.MILLISECONDS);
}

Observable<String> asyncCompuatation(String string) {
    return Observable.just(string)
            .subscribeOn(Schedulers.computation())
            .delay(random.nextInt(1000), TimeUnit.MILLISECONDS)
            .doOnNext(number -> System.out.println("Computing " + number + " on thread: " + Thread.currentThread()));
}

输出验证:

Executing request for 7 on thread: Thread[RxCachedThreadScheduler-7,5,main] Executing request for 6 on thread: Thread[RxCachedThreadScheduler-6,5,main] Executing request for 5 on thread: Thread[RxCachedThreadScheduler-5,5,main] Executing request for 1 on thread: Thread[RxCachedThreadScheduler-1,5,main] Executing request for 4 on thread: Thread[RxCachedThreadScheduler-4,5,main] Executing request for 3 on thread: Thread[RxCachedThreadScheduler-3,5,main] Executing request for 8 on thread: Thread[RxCachedThreadScheduler-8,5,main] Executing request for 2 on thread: Thread[RxCachedThreadScheduler-2,5,main] Executing request for 9 on thread: Thread[RxCachedThreadScheduler-9,5,main] Executing request for 10 on thread: Thread[RxCachedThreadScheduler-10,5,main] Computing Item nr 7.1 on thread: Thread[RxComputationThreadPool-5,5,main] Computing Item nr 10.2 on thread: Thread[RxComputationThreadPool-2,5,main] Computing Item nr 6.2 on thread: Thread[RxComputationThreadPool-1,5,main] Computing Item nr 3.1 on thread: Thread[RxComputationThreadPool-7,5,main] Computing Item nr 4.1 on thread: Thread[RxComputationThreadPool-7,5,main] Computing Item nr 3.2 on thread: Thread[RxComputationThreadPool-1,5,main] Computing Item nr 6.1 on thread: Thread[RxComputationThreadPool-7,5,main] Computing Item nr 2.1 on thread: Thread[RxComputationThreadPool-7,5,main] Computing Item nr 5.2 on thread: Thread[RxComputationThreadPool-2,5,main] Computing Item nr 5.1 on thread: Thread[RxComputationThreadPool-5,5,main] Computing Item nr 7.2 on thread: Thread[RxComputationThreadPool-2,5,main] Computing Item nr 2.2 on thread: Thread[RxComputationThreadPool-1,5,main] Computing Item nr 10.1 on thread: Thread[RxComputationThreadPool-5,5,main] Computing Item nr 9.1 on thread: Thread[RxComputationThreadPool-5,5,main] Computing Item nr 4.2 on thread: Thread[RxComputationThreadPool-1,5,main] Computing Item nr 9.2 on thread: Thread[RxComputationThreadPool-2,5,main] Computing Item nr 8.1 on thread: Thread[RxComputationThreadPool-5,5,main] Computing Item nr 8.2 on thread: Thread[RxComputationThreadPool-2,5,main] Computing Item nr 1.1 on thread: Thread[RxComputationThreadPool-7,5,main] Computing Item nr 1.2 on thread: Thread[RxComputationThreadPool-1,5,main] onComplete

答案 1 :(得分:1)

好的,不确定我的问题是否100%正确,但这里是我要做的事情的粗略草图......我相信你希望Subject作为缓存的中间级别当你打电话取消订阅时,不要打断实际请求。

1)假设您有2个Retrofit Observables。

2)在startRequests()中,您需要订阅它们(在您需要的某些调度程序上),应用doOnNext运算符并将数据委托给subject。因此,主题将从API获得2个滴答数据。

3)订阅您的主题,您将收到2个数据滴答。

基本上没有必要等待完成,你只需要接收N个onNext ticks。 但是如果你想要一些指标表明所有请求都已完成,你可以例如合并所有改进的observable,并将所有事件委托给subject,这样它最终会收到N个onNext ticks和onComplete。

答案 2 :(得分:0)

我认为使用Subjects是不必要的并发症。 你只需使用flatMap()并使用Completable转移到toCompletable(),你没有提到你的特定循环是如何工作的,但假设你有一些循环列表查询就像这样,startRequest(data)返回Retrofit查询Observable

 List<Data> list = ...;
    Observable.from(list)
            .flatMap(new Func1<Data, Observable<Result>>() {
                @Override
                public Observable<Result> call(Data data) {
                    return startRequest(data);
                }
            }).toCompletable();

关于您的第二个请求,执行更多请求取决于结果,在这种情况下,您可能希望使用toList()收集所有请求,您将收到一个onNext()通知,然后您可以对其进行过滤所有,并在您想要请求更多数据时获取发出项目的Observable

 List<Data> list =...;
    Observable.from(list)
            .flatMap(new Func1<Data, Observable<Result>>() {
                @Override
                public Observable<Result> call(Data data) {
                    return startRequest(data);
                }
            })
            .toList()
            .filter(new Func1<List<Result>, Boolean>() {
                @Override
                public Boolean call(List<Result> results) {
                    return shouldRequestMore(results);
                }
            });