Когда вы используете map vs flatMap в RxJava?
когда вы используете map vs flatMap в RxJava?
скажем, например, мы хотим сопоставить файлы, содержащие JSON, в строки, содержащие JSON--
используя карту, мы должны как-то справиться с исключением. Но как?:
Observable.from(jsonFile).map(new Func1<File, String>() {
@Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// So Exception. What to do ?
}
return null; // Not good :(
}
});
используя flatMap, это гораздо более подробно, но мы можем направить проблему вниз по цепочке наблюдаемых и обработать ошибку, если мы выберем где-то еще и даже повторим:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
@Override public Observable<String> call(final File file) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
subscriber.onNext(json);
subscriber.onCompleted();
} catch (FileNotFoundException e) {
subscriber.onError(e);
}
}
});
}
});
мне нравится простота карты, но обработка ошибок flatmap (не многословие). Я не видел никаких лучших практик по этому плаванию, и мне любопытно, как это используется на практике.
9 ответов:
mapпреобразование одного события к другому.flatMapпреобразование одного события в ноль или более событий. (это взято из IntroToRx)как вы хотите, чтобы преобразовать json в объект, используя карту должно быть достаточно.
работа с FileNotFoundException-это еще одна проблема (использование map или flatmap не решит эту проблему).
чтобы решить вашу проблему исключения, просто бросьте его с непроверенным исключением : RX вызовет обработчик onError для тебя.
Observable.from(jsonFile).map(new Func1<File, String>() { @Override public String call(File file) { try { return new Gson().toJson(new FileReader(file), Object.class); } catch (FileNotFoundException e) { // this exception is a part of rx-java throw OnErrorThrowable.addValueAsLastCause(e, file); } } });точно такая же версия с flatmap:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(File file) { try { return Observable.just(new Gson().toJson(new FileReader(file), Object.class)); } catch (FileNotFoundException e) { // this static method is a part of rx-java. It will return an exception which is associated to the value. throw OnErrorThrowable.addValueAsLastCause(e, file); // alternatively, you can return Obersable.empty(); instead of throwing exception } } });вы также можете вернуть в версии flatMap новую наблюдаемую, которая является просто ошибкой.
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(File file) { try { return Observable.just(new Gson().toJson(new FileReader(file), Object.class)); } catch (FileNotFoundException e) { return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file)); } } });
FlatMap ведет себя очень похоже на карту, разница в том, что функция, которую он применяет возвращает сам наблюдаемый, поэтому он идеально подходит для сопоставления асинхронных операций.
в практическом смысле функция Map applies просто выполняет преобразование над цепным ответом (не возвращая наблюдаемый); в то время как функция FlatMap applies возвращает
Observable<T>, именно поэтому FlatMap рекомендуется, если вы планируете сделать асинхронный вызов внутри метод.резюме:
- карта возвращает объект типа T
- FlatMap возвращает наблюдаемый.
наглядный пример можно увидеть здесь:http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk .
Couchbase Java 2.Клиент X использует Rx для обеспечения асинхронных вызовов удобным способом. Поскольку он использует Rx, у него есть методы map и FlatMap, объяснение в их документации может быть полезно понять общую концепцию.
для обработки ошибок, переопределить метод onerror на ваш абонента.
Subscriber<String> mySubscriber = new Subscriber<String>() { @Override public void onNext(String s) { System.out.println(s); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } };Это может помочь взглянуть на этот документ: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
хороший источник о том, как управлять ошибками с RX можно найти по адресу:https://gist.github.com/daschl/db9fcc9d2b932115b679
в вашем случае я думаю, что вам нужна карта, поскольку там только 1 вход и 1 выход.
функция map-supplied просто принимает элемент и возвращает элемент, который будет излучаться дальше (только один раз) вниз.
функция flatMap-supplied принимает элемент, а затем возвращает "Observable", то есть каждый элемент нового "Observable" будет излучаться отдельно дальше вниз.
может быть код прояснит вещи для вы.
//START DIFFERENCE BETWEEN MAP AND FLATMAP Observable.just("item1") .map( str -> { System.out.println("inside the map " + str); return str; }) .subscribe(System.out::println); Observable.just("item2") .flatMap( str -> { System.out.println("inside the flatMap " + str); return Observable.just(str + "+", str + "++" , str + "+++"); }) .subscribe(System.out::println); //END DIFFERENCE BETWEEN MAP AND FLATMAPвыход:
inside the map item1 item1 inside the flatMap item2 item2+ item2++ item2+++
то, как я думаю об этом, что вы используете
flatMapкогда функция, которую вы хотели поставить внутриmap()возвращаетObservable. В этом случае вы все равно можете попробовать использоватьmap()но это было бы непрактично. Позвольте мне попытаться объяснить, почему.если в таком случае вы решили придерживаться
map, вы получитеObservable<Observable<Something>>. Например, в вашем случае, если мы использовали воображаемую библиотеку RxGson, которая вернулаObservable<String>СtoJson()метод (вместо простого возврата aString) это будет выглядеть так:Observable.from(jsonFile).map(new Func1<File, Observable<String>>() { @Override public Observable<String>> call(File file) { return new RxGson().toJson(new FileReader(file), Object.class); } }); // you get Observable<Observable<String>> hereв этот момент было бы довольно сложно
слияние это то, что мы хотим. Слияние в основном принимает кучу наблюдаемых и испускает всякий раз, когда какой-либо из них испускает. (Многие люди будут спорить переключатель было бы лучше по умолчанию. Но если вы излучаете только одно значение, это не имеет значения в любом случае.)subscribe()к такому наблюдаемому. Внутри него вы получитеObservable<String>к которому вам снова нужно будетsubscribe()чтобы получить значение. Что не практично и не приятно смотреть.Итак, изменив наш предыдущий фрагмент, мы получим:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() { @Override public Observable<String>> call(File file) { return new RxGson().toJson(new FileReader(file), Object.class); } }).merge(); // you get Observable<String> hereэто намного полезнее, потому что подписка на это (или отображение, или фильтрация, или...) ты просто получить
Stringзначение. (Также, заметьте, такой вариантmerge()не существует в RxJava, но если вы понимаете идею слияния, то я надеюсь, вы также понимаете, как это будет работать.)так в основном потому, что такие
merge()вероятно, должен быть полезен только тогда, когда он преуспевает вmap()возвращая наблюдаемый и поэтому вам не нужно вводить это снова и снова,flatMap()был создан как стенография. Он применяет функцию отображения так же, как обычныйmap()будет, но позже вместо того, чтобы излучать возвращенные значения, он также "сглаживает" (или сливает) их.это общий случай использования. Это наиболее полезно в кодовой базе, которая использует Rx по всему месту, и у вас есть много методов, возвращающих наблюдаемые, которые вы хотите связать с другими методами, возвращающими наблюдаемые.
в вашем случае использования это также полезно, потому что
map()можно преобразовать только одно значение, испускаемое вonNext()в другое значение, выбрасываемых вonNext(). Но это не удается преобразовать его в несколько значений, нет значения вообще или ошибка. И как akarnokd написал в своем ответе (и имейте в виду, что он намного умнее меня, вероятно, в целом, но, по крайней мере, когда дело доходит до RxJava) вы не должны бросать исключения из своегоmap(). Так что вместо этого вы можете использоватьflatMap()иreturn Observable.just(value);когда все идет хорошо, но
return Observable.error(exception);когда что-то не удается.
См. его ответ для полного фрагмента: https://stackoverflow.com/a/30330772/1402641
вот простой thumb-rule что я использую помогите мне решить, как когда использовать
flatMap()overmap()в RxObservable.как только вы придете к решению, что вы собираетесь использовать
mapпреобразование, вы бы написали свой код преобразования, чтобы вернуть какой-то объект правильно?если то, что вы возвращаетесь в качестве конечного результата преобразования:
ненаблюдаемый объект, то вы бы использовали просто
map(). Иmap()обертывает этот объект в наблюдаемый и испускает его.an
Observableобъект, то вы бы использовалиflatMap(). ИflatMap()разворачивает наблюдаемое, выбирает возвращаемый объект, обертывает его своим собственным наблюдаемым и испускает его.скажем, например, у нас есть метод titleCase(String inputParam), который возвращает объект Cased String входного параметра. Возвращаемый тип этого метода может быть
StringилиObservable<String>.
если возвращаемый тип
titleCase(..)должны были быть простоString, тогда вы могли бы использоватьmap(s -> titleCase(s))если возвращаемый тип
titleCase(..)должны бытьObservable<String>, тогда вы могли бы использоватьflatMap(s -> titleCase(s))надеюсь, что это проясняет.
Я просто хотел добавить, что с
flatMap, вам действительно не нужно использовать свой собственный пользовательский наблюдаемый внутри функции, и вы можете полагаться на стандартные заводские методы / операторы:Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(final File file) { try { String json = new Gson().toJson(new FileReader(file), Object.class); return Observable.just(json); } catch (FileNotFoundException ex) { return Observable.<String>error(ex); } } });Как правило, вы должны избегать исключения (Runtime-) из методов onXXX и обратных вызовов, если это возможно, даже если мы разместили столько гарантий, сколько могли в RxJava.
вопрос когда вы используете map vs flatMap в RxJava?. И я думаю, что простая демонстрация более конкретна.
если вы хотите конвертировать элемент, выделенный в другой тип, в вашем случае преобразование файла в строку, карту и flatMap может работать. Но я предпочитаю оператор карты, потому что это более четко.
в каком-то месте,flatMapможет делать магическую работу, ноmapне могу. Например, я хочу получить пользовательские данные, но мне нужно сначала получить его ID, когда пользователь войдите в систему. Очевидно, мне нужно два запроса и они в порядке.давайте начнем.
Observable<LoginResponse> login(String email, String password); Observable<UserInfo> fetchUserInfo(String userId);вот два метода, один для входа возвращается
Response, и еще один для получения информации о пользователе.login(email, password) .flatMap(response -> fetchUserInfo(response.id)) .subscribe(userInfo -> { // get user info and you update ui now });как вы видите, в функции flatMap применяется, сначала я получаю идентификатор пользователя от получить информацию о пользователе. Когда два запроса завершены, мы можем выполнить нашу работу, такую как обновление пользовательского интерфейса или сохранение данных в базе данных.
если вы используетеmapвы не можете напишите такой хороший код. Одним словом,flatMapможет помочь нам сериализовать запросы.
в этом сценарии используйте карту, вам не нужен новый наблюдаемый для нее.
вы должны использовать исключения.propagate, который является оболочкой, поэтому вы можете отправить эти проверенные исключения в механизм rx
Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() { @Override public String call(File file) { try { return new Gson().toJson(new FileReader(file), Object.class); } catch (FileNotFoundException e) { throw Exceptions.propagate(t); /will propagate it as error } } });затем вы должны обработать эту ошибку в абонента
obs.subscribe(new Subscriber<String>() { @Override public void onNext(String s) { //valid result } @Override public void onCompleted() { } @Override public void onError(Throwable e) { //e might be the FileNotFoundException you got } };);есть отличный пост для него:http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
в некоторых случаях вы можете получить цепочку наблюдаемых, в которой ваш наблюдаемый вернет другой наблюдаемый. вид "flatmap" разворачивает второй наблюдаемый, который похоронен в первом, и позволяет вам напрямую обращаться к данным, которые второй наблюдаемый выплевывает во время подписки.
Comments