Kết hợp danh sách các Có thể quan sát và đợi cho đến khi tất cả hoàn thành


91

TL; DR Làm thế nào để chuyển đổi Task.whenAll(List<Task>)thành RxJava?

Mã hiện tại của tôi sử dụng Bolts để tạo danh sách các tác vụ không đồng bộ và đợi cho đến khi tất cả các tác vụ đó kết thúc trước khi thực hiện các bước khác. Về cơ bản, nó xây dựng một List<Task>và trả về một đơn Taskđược đánh dấu là đã hoàn thành khi tất cả các nhiệm vụ trong danh sách hoàn thành, theo ví dụ trên trang Bolts .

Tôi đang tìm cách thay thế Boltsbằng RxJavavà tôi giả định rằng phương pháp này tạo danh sách các tác vụ không đồng bộ (kích thước không được biết trước) và gói tất cả chúng thành một tác vụ duy nhất Observablecó thể thực hiện được, nhưng tôi không biết làm thế nào.

Tôi đã cố gắng nhìn vào merge, zip, concatvv ... nhưng không thể có được để làm việc trên List<Observable>mà tôi muốn được xây dựng như tất cả họ đều dường như hướng tới làm việc trên chỉ hai Observablestại một thời điểm nếu tôi hiểu các tài liệu một cách chính xác.

Tôi đang cố gắng tìm hiểu RxJavavà vẫn còn rất mới với nó, vì vậy hãy tha thứ cho tôi nếu đây là một câu hỏi rõ ràng hoặc được giải thích trong tài liệu ở đâu đó; Tôi đã thử tìm kiếm. Bất kì sự trợ giúp nào đều được đánh giá cao.

Câu trả lời:


73

Có vẻ như bạn đang tìm toán tử Zip .

Có một số cách khác nhau để sử dụng nó, vì vậy hãy xem một ví dụ. Giả sử chúng ta có một số vật thể quan sát đơn giản thuộc các loại khác nhau:

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

Cách đơn giản nhất để đợi tất cả chúng là như sau:

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

Lưu ý rằng trong hàm zip, các tham số có loại cụ thể tương ứng với loại của các đối tượng quan sát được nén.

Cũng có thể nén danh sách các vật có thể quan sát, hoặc trực tiếp:

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

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

... hoặc bằng cách gói danh sách thành Observable<Observable<?>>:

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

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

Tuy nhiên, trong cả hai trường hợp này, hàm zip chỉ có thể chấp nhận một Object[]tham số duy nhất vì các loại có thể quan sát trong danh sách không được biết trước cũng như số lượng của chúng. Điều này có nghĩa là hàm zip sẽ phải kiểm tra số lượng tham số và ép chúng cho phù hợp.

Bất kể, tất cả các ví dụ trên cuối cùng sẽ in 1 Blah true

CHỈNH SỬA: Khi sử dụng Zip, hãy đảm bảo rằng Observablestất cả các tệp đang được nén đều phát ra cùng một số mục. Trong các ví dụ trên, cả ba vật thể quan sát đều phát ra một mục duy nhất. Nếu chúng tôi thay đổi chúng thành một cái gì đó như thế này:

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

Sau đó 1, Blah, True2, Hello, Truesẽ là các mục duy nhất được chuyển vào (các) hàm zip. Mục 3sẽ không bao giờ được nén vì các vật quan sát khác đã hoàn thành.


9
Điều này sẽ không hoạt động nếu một trong các cuộc gọi không thành công. Trong trường hợp đó, tất cả các cuộc gọi sẽ bị mất.
StarWind0

1
@ StarWind0 bạn có thể bỏ qua lỗi bằng cách sử dụng onErrorResumeNext, ví dụ:Observable.zip(ob1, ob2........).onErrorResumeNext(Observable.<String>empty())
vuhung3990

Điều gì sẽ xảy ra nếu tôi có 100 vật thể quan sát?
Krzysztof Kubicki

80

Bạn có thể sử dụng flatMaptrong trường hợp bạn có thành phần tác vụ động. Một cái gì đó như thế này:

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);
}

Một ví dụ điển hình khác về thực thi song song

Lưu ý: Tôi không thực sự biết yêu cầu của bạn đối với việc xử lý lỗi. Ví dụ phải làm gì nếu chỉ một nhiệm vụ không thành công. Tôi nghĩ bạn nên xác minh kịch bản này.


17
Đây phải là câu trả lời được chấp nhận khi câu hỏi nói rằng "khi tất cả các nhiệm vụ trong danh sách hoàn thành". zipthông báo về việc hoàn thành ngay sau khi một trong các nhiệm vụ được hoàn thành và do đó không được áp dụng.
user3707125

1
@MyDogTom: Bạn có thể cập nhật câu trả lời bằng phiên bản Cú pháp Java7 (Không phải lambda) không?
sanedroid

3
@PoojaGaikwad Với lambda, nó dễ đọc hơn. Chỉ cần thay thế lambda đầu tiên với new Func1<Observable<Boolean>, Observable<Boolean>>()...và thứ hai vớinew Func1<List<Boolean>, Boolean>()
MyDogTom

@soshial RxJava 2 là điều tồi tệ nhất từng xảy ra với RxJava, vâng
egorikem

15

Trong số các gợi ý được đề xuất, zip () thực sự kết hợp các kết quả có thể quan sát được với nhau, có thể có hoặc có thể không phải là điều mong muốn, nhưng không được hỏi trong câu hỏi. Trong câu hỏi, tất cả những gì mong muốn là thực hiện từng thao tác, từng thao tác một hoặc song song (không được chỉ rõ, nhưng ví dụ về Bu lông liên kết là về thực hiện song song). Ngoài ra, zip () sẽ hoàn thành ngay lập tức khi bất kỳ mục nào có thể quan sát được hoàn thành, vì vậy nó vi phạm các yêu cầu.

Để thực thi song song các Observables, flatMap () được trình bày trong câu trả lời khác là tốt, nhưng merge () sẽ dễ hiểu hơn. Lưu ý rằng hợp nhất sẽ thoát do lỗi của bất kỳ Quan sát nào, nếu bạn muốn hoãn quá trình thoát cho đến khi tất cả các Có thể quan sát kết thúc, bạn nên xem mergeDelayError () .

Đối với từng cái một, tôi nghĩ nên sử dụng phương thức tĩnh Observable.concat () . Javadoc của nó nói như thế này:

concat (java.lang.Iterable> sequences) Làm phẳng một Lặp có thể quan sát thành một Có thể quan sát, cái này đến cái khác, mà không cần xen kẽ chúng

âm thanh giống như những gì bạn đang theo đuổi nếu bạn không muốn thực hiện song song.

Ngoài ra, nếu bạn chỉ quan tâm đến việc hoàn thành nhiệm vụ của mình chứ không phải trả về giá trị, bạn có thể nên xem xét Hoàn thành thay vì Có thể quan sát .

TLDR: để thực thi từng tác vụ và sự kiện hoàn thành khi chúng hoàn thành, tôi nghĩ Completable.concat () là phù hợp nhất. Để thực thi song song, Completable.merge () hoặc Completable.mergeDelayError () có vẻ giống như giải pháp. Cái trước sẽ dừng ngay lập tức đối với bất kỳ lỗi nào trên bất kỳ bảng hoàn thành nào, cái sau sẽ thực thi tất cả chúng ngay cả khi một trong số chúng có lỗi và chỉ sau đó báo cáo lỗi.


2

Có thể bạn đã xem ziptoán tử hoạt động với 2 Observables.

Ngoài ra còn có phương thức tĩnh Observable.zip. Nó có một biểu mẫu hữu ích cho bạn:

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

Bạn có thể kiểm tra javadoc để biết thêm.


2

Với Kotlin

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

})

Điều quan trọng là phải đặt kiểu cho các đối số của hàm, nếu không bạn sẽ gặp lỗi biên dịch

Kiểu đối số cuối cùng thay đổi với số đối số: BiFunction cho 2 Function3 cho 3 Function4 cho 4 ...


1

Tôi đang viết một số mã hỗ trợ tính toán trong Kotlin với JavaRx Observables và RxKotlin. Tôi muốn quan sát một danh sách có thể quan sát sẽ được hoàn thành và trong thời gian chờ đợi, hãy cập nhật cho tôi tiến trình và kết quả mới nhất. Cuối cùng, nó trả về kết quả tính toán tốt nhất. Một yêu cầu bổ sung là chạy Observables song song để sử dụng tất cả các lõi cpu của tôi. Tôi đã kết thúc với giải pháp này:

@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)
            }
        )
    }
}

không quen thuộc với RxKotlin hoặc @Volatile, nhưng điều này sẽ hoạt động như thế nào nếu điều này được gọi bởi một số chủ đề cùng một lúc? Điều gì sẽ xảy ra với kết quả?
eis

0

Tôi đã gặp sự cố tương tự, tôi cần tìm nạp các mục tìm kiếm từ lệnh gọi nghỉ đồng thời tích hợp các đề xuất đã lưu từ RecentSearchProvider.AUTHORITY và kết hợp chúng với nhau thành một danh sách thống nhất. Tôi đang cố gắng sử dụng giải pháp @MyDogTom, rất tiếc là không có Observable.from trong RxJava. Sau một số nghiên cứu, tôi đã có một giải pháp phù hợp với tôi.

 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) }
}

Tôi đã tạo một cái có thể quan sát được từ mảng các cái có thể quan sát được chứa danh sách các đề xuất và kết quả từ internet tùy thuộc vào truy vấn. Sau đó, bạn chỉ cần xem qua các tác vụ đó với flatMapIterable và chạy chúng bằng bản đồ phẳng, đặt kết quả vào mảng, sau này có thể được tìm nạp vào chế độ xem tái chế.


0

Nếu bạn sử dụng Project Reactor, bạn có thể sử dụng Mono.when.

Mono.when(publisher1, publisher2)
.map(i-> {
    System.out.println("everything is done!");
    return i;
}).block()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.