Observable 목록을 결합하고 모두 완료 될 때까지 기다립니다.


91

TL; DR 어떻게 변환하기 Task.whenAll(List<Task>)RxJava?

내 기존 코드는 Bolts를 사용하여 비동기 작업 목록을 작성하고 다른 단계를 수행하기 전에 모든 작업이 완료 될 때까지 기다립니다. 기본적으로 Bolts 사이트예제에 따라 a를 빌드 하고 목록의 모든 작업이 완료되면 완료된 것으로 표시된 List<Task>단일 Task을 반환합니다 .

나는 대체 할 찾고 있어요 BoltsRxJava내가 비동기 작업 목록 (안 미리 알 크기)을 구축하고 싱글로 모든 포장의이 방법 있으리라 믿고있어 Observable가능하다, 그러나 나는 방법을 모른다.

내가보고 시도했습니다 merge, zip, concat등 ...하지만에 동작하지 않습니다 List<Observable>모두가 두를 작업에 기어드 것 같다 나는이 구축 될 거라고 Observables내가 제대로 문서를 이해한다면 한 번에.

나는 배우려고 노력하고 RxJava있으며 아직 매우 새로운 것이므로 이것이 명백한 질문이거나 문서 어딘가에 설명되어 있다면 나를 용서하십시오. 나는 수색을 시도했다. 어떤 도움이라도 대단히 감사하겠습니다.

답변:


73

Zip 연산자를 찾고있는 것 같습니다 .

사용 방법에는 몇 가지가 있으므로 예를 살펴 보겠습니다. 다양한 유형의 몇 가지 간단한 관찰 가능 항목이 있다고 가정 해 보겠습니다.

Observable<Integer> obs1 = Observable.just(1);
Observable<String> obs2 = Observable.just("Blah");
Observable<Boolean> obs3 = Observable.just(true);

모두를 기다리는 가장 간단한 방법은 다음과 같습니다.

Observable.zip(obs1, obs2, obs3, (Integer i, String s, Boolean b) -> i + " " + s + " " + b)
.subscribe(str -> System.out.println(str));

zip 함수에서 매개 변수에는 압축되는 옵저버 블 유형에 해당하는 구체적인 유형이 있습니다.

관찰 가능 항목 목록을 직접 압축하는 것도 가능합니다.

List<Observable<?>> obsList = Arrays.asList(obs1, obs2, obs3);

Observable.zip(obsList, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

... 또는 목록을 다음으로 래핑합니다 Observable<Observable<?>>.

Observable<Observable<?>> obsObs = Observable.from(obsList);

Observable.zip(obsObs, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

그러나이 두 경우 모두 Object[]목록에있는 옵저버 블 유형과 그 수를 미리 알 수 없기 때문에 zip 함수는 단일 매개 변수 만 허용 할 수 있습니다. 이것은 zip 함수가 매개 변수의 수를 확인하고 그에 따라 캐스트해야 함을 의미합니다.

어쨌든 위의 모든 예는 결국 인쇄됩니다. 1 Blah true

편집 : Zip을 사용할 때 Observables압축되는 모든 항목이 동일한 수의 항목을 방출 하는지 확인하십시오 . 위의 예에서 세 가지 Observable은 모두 단일 항목을 내보냈습니다. 다음과 같이 변경하면 :

Observable<Integer> obs1 = Observable.from(new Integer[]{1,2,3}); //Emits three items
Observable<String> obs2 = Observable.from(new String[]{"Blah","Hello"}); //Emits two items
Observable<Boolean> obs3 = Observable.from(new Boolean[]{true,true}); //Emits two items

그런 다음 1, Blah, True2, Hello, True압축 기능 (들)에 전달 된 항목 만 할 것이다. 3다른 관찰 가능 항목이 완료되었으므로 항목 은 압축되지 않습니다.


9
호출 중 하나가 실패하면 작동하지 않습니다. 이 경우 모든 통화가 손실됩니다.
StarWind0 2016

1
StarWind0 @ 당신은을 사용하여 오류를 건너 뛸 수 있습니다 onErrorResumeNext, 예 :Observable.zip(ob1, ob2........).onErrorResumeNext(Observable.<String>empty())
vuhung3990

관찰 가능 항목이 100 개 있으면 어떻게 되나요?
Krzysztof Kubicki

79

flatMap동적 작업 구성이있는 경우 사용할 수 있습니다 . 이 같은:

public Observable<Boolean> whenAll(List<Observable<Boolean>> tasks) {
    return Observable.from(tasks)
            //execute in parallel
            .flatMap(task -> task.observeOn(Schedulers.computation()))
            //wait, until all task are executed
            //be aware, all your observable should emit onComplemete event
            //otherwise you will wait forever
            .toList()
            //could implement more intelligent logic. eg. check that everything is successful
            .map(results -> true);
}

병렬 실행의 또 다른 좋은 예

참고 : 오류 처리에 대한 요구 사항을 잘 모릅니다. 예를 들어 하나의 작업 만 실패 할 경우 수행 할 작업. 이 시나리오를 확인해야한다고 생각합니다.


16
질문에 "목록의 모든 작업이 완료 될 때"라는 질문을 고려할 때이 답변이 허용되어야합니다. zip작업 중 하나가 완료 되 자마자 완료를 알리므로 적용 할 수 없습니다.
user3707125

1
@MyDogTom : Java7 구문 (람다가 아님) 버전으로 답변을 업데이트 할 수 있습니까?
sanedroid

3
@PoojaGaikwad 람다를 사용하면 더 읽기 쉽습니다. 단지와 최초의 람다 교체 new Func1<Observable<Boolean>, Observable<Boolean>>()...로하고 두 번째 한new Func1<List<Boolean>, Boolean>()
MyDogTom

@soshial RxJava 2는 RxJava에서 일어난 최악의 일입니다. 예
egorikem

15

제안 된 제안 중 zip ()은 실제로 관찰 가능한 결과를 서로 결합하는데, 이는 원하는 결과 일 수도 있고 아닐 수도 있지만 질문에서 묻지 않았습니다. 질문에서 원하는 것은 각 작업을 하나씩 또는 병렬로 실행하는 것이 었습니다 (지정되지 않았지만 연결된 Bolts 예제는 병렬 실행에 관한 것임). 또한 zip ()은 관찰 가능 항목이 완료되면 즉시 완료되므로 요구 사항을 위반하는 것입니다.

Observable의 병렬 실행의 경우 다른 답변에 제시된 flatMap () 좋지만 merge () 는 더 간단합니다. 모든 Observable이 완료 될 때까지 종료를 연기한다면 mergeDelayError ()를 보고 있어야합니다 .

하나씩의 경우 Observable.concat () 정적 메서드를 사용해야 한다고 생각 합니다. javadoc은 다음과 같이 상태가됩니다.

concat (java.lang.Iterable> 시퀀스) Observable의 Iterable을 인터리빙하지 않고 하나의 Observable로 평평하게 만듭니다.

병렬 실행을 원하지 않는다면 당신이 추구하는 것처럼 들립니다.

또한 값을 반환하지 않고 작업 완료에만 관심이 있다면 Observable 대신 Completable을 살펴 봐야 할 것입니다 .

TLDR : 작업을 하나씩 실행하고 완료시 완료 이벤트를 수행하려면 Completable.concat ()이 가장 적합하다고 생각합니다. 병렬 실행의 경우 Completable.merge () 또는 Completable.mergeDelayError ()는 솔루션처럼 들립니다. 전자는 완료 가능한 모든 오류가 발생하면 즉시 중지되고 후자는 오류가 있더라도 모두 실행 한 다음 오류를보고합니다.


2

zip2 개의 Observable과 함께 작동 하는 연산자를 보았을 것입니다 .

정적 메서드도 Observable.zip있습니다. 그것은 당신에게 유용한 하나의 양식을 가지고 있습니다.

zip(java.lang.Iterable<? extends Observable<?>> ws, FuncN<? extends R> zipFunction)

자세한 내용javadoc을 확인하십시오 .


2

Kotlin 사용

Observable.zip(obs1, obs2, BiFunction { t1 : Boolean, t2:Boolean ->

})

함수의 인수 유형을 설정하는 것이 중요합니다. 그렇지 않으면 컴파일 오류가 발생합니다.

인수 수에 따라 마지막 인수 유형 변경 : BiFunction for 2 Function3 for 3 Function4 for 4 ...


1

JavaRx Observables 및 RxKotlin을 사용하여 Kotlin에서 계산 힙 코드를 작성하고 있습니다. 완료 할 관찰 가능 항목 목록을 관찰하고 그 동안 진행 상황 및 최신 결과에 대한 업데이트를 제공하고 싶습니다. 마지막에는 최상의 계산 결과를 반환합니다. 추가 요구 사항은 모든 CPU 코어를 사용하기 위해 Observable을 병렬로 실행하는 것이 었습니다. 이 솔루션으로 끝났습니다.

@Volatile var results: MutableList<CalculationResult> = mutableListOf()

fun doALotOfCalculations(listOfCalculations: List<Calculation>): Observable<Pair<String, CalculationResult>> {

    return Observable.create { subscriber ->
        Observable.concatEager(listOfCalculations.map { calculation: Calculation ->
            doCalculation(calculation).subscribeOn(Schedulers.computation()) // function doCalculation returns an Observable with only one result
        }).subscribeBy(
            onNext = {
                results.add(it)
                subscriber.onNext(Pair("A calculation is ready", it))

            },
            onComplete = {
                subscriber.onNext(Pair("Finished: ${results.size}", findBestCalculation(results)) 
                subscriber.onComplete()
            },
            onError = {
                subscriber.onError(it)
            }
        )
    }
}

RxKotlin 또는에 익숙하지 @Volatile않지만 동시에 여러 스레드에서 호출되는 경우 어떻게 작동합니까? 결과는 어떻게됩니까?
eis

0

비슷한 문제가 있었기 때문에 Rest Call에서 검색 항목을 가져 오는 동시에 RecentSearchProvider.AUTHORITY에서 저장된 제안을 통합하고 하나의 통합 목록으로 결합해야했습니다. 나는 @MyDogTom 솔루션을 사용하려고했지만 불행히도 RxJava에는 Observable.from이 없습니다. 몇 가지 연구 끝에 저에게 도움이되는 솔루션을 얻었습니다.

 fun getSearchedResultsSuggestions(context : Context, query : String) : Single<ArrayList<ArrayList<SearchItem>>>
{
    val fetchedItems = ArrayList<Observable<ArrayList<SearchItem>>>(0)
    fetchedItems.add(fetchSearchSuggestions(context,query).toObservable())
    fetchedItems.add(getSearchResults(query).toObservable())

    return Observable.fromArray(fetchedItems)
        .flatMapIterable { data->data }
        .flatMap {task -> task.observeOn(Schedulers.io())}
        .toList()
        .map { ArrayList(it) }
}

쿼리에 따라 인터넷의 제안 및 결과 목록을 포함하는 Observable 배열에서 Observable을 만들었습니다. 그런 다음 flatMapIterable을 사용하여 해당 작업을 살펴보고 flatmap을 사용하여 실행 한 다음 결과를 배열에 배치하면 나중에 재활용보기로 가져올 수 있습니다.


0

Project Reactor를 사용하는 경우 Mono.when.

Mono.when(publisher1, publisher2)
.map(i-> {
    System.out.println("everything is done!");
    return i;
}).block()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.