Когда вы используете map vs flatMap в RxJava?
когда вы используете map vs flatMap в RxJava?
скажем, например, мы хотим сопоставить файлы, содержащие JSON, в строки, содержащие json--
используя map, мы должны как-то справиться с исключением. Но как?:
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 в объект, достаточно использовать map.
работа с 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 ведет себя очень похоже на карту, разница в том, что функция, которую он применяет возвращает наблюдаемый сам, поэтому он идеально подходит для сопоставления асинхронных операций.
в практическом смысле функциональная карта применяется просто делает преобразование над цепным ответом (не возвращая наблюдаемое); в то время как функция FlatMap applies возвращает Observable<T>
, поэтому FlatMap рекомендуется, если вы планируете сделать асинхронный вызов внутри метод.
резюме:
- Map возвращает объект типа 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()
метод (вместо того, чтобы просто возвращать String
) это выглядело бы так:
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()
чтобы получить значение. Что не практично и не приятно смотреть.
поэтому, чтобы сделать его полезным, одна идея - "сгладить" это наблюдаемое из наблюдаемых (вы можете начать видеть, откуда происходит имя _flat_Map). RxJava предоставляет несколько способов сгладить наблюдаемые и для простоты предположим слияние это то, что мы хотим. Слияние в основном принимает кучу наблюдаемых и излучает всякий раз, когда любой из них излучает. (Многие люди будут спорить переключатель было бы лучше по умолчанию. Но если вы излучаете только одно значение,это не имеет значения.)
таким образом, изменяя наш предыдущий фрагмент, мы получим:
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()
над map()
в .
как только вы придете к решению, что вы собираетесь использовать map
преобразование, вы напишете код преобразования, чтобы вернуть какой-то объект правильно?
если то, что вы возвращаетесь в качестве конечного результата преобразования:
ненаблюдаемый объект, тогда вы бы использовали только
map()
. Иmap()
обертывает этот объект в наблюдаемый и излучает его.an
Я просто хотел добавить, что с 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 может работать. Но я предпочитаю map operator, потому что это более ясно.
в каком-то месте,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
может помочь нам сериализовать запросы.в этом сценарии используйте map, вам не нужен новый наблюдаемый для него.
вы должны использовать исключения.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" разворачивает второй наблюдаемый, который похоронен в первом, и позволяет вам напрямую получить доступ к данным, которые второй наблюдаемый выплевывает при подписке.