¿Cuándo usas map
vs flatMap
en RxJava ?
Digamos, por ejemplo, que queremos asignar archivos que contienen JSON en cadenas que contienen el JSON:
Usando map
, tenemos que lidiar con el de Exception
alguna manera. ¿Pero cómo?:
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 :(
}
});
El uso flatMap
es mucho más detallado, pero podemos reenviar el problema por la cadena Observables
y manejar el error si elegimos otro lugar e incluso volvemos a intentarlo:
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);
}
}
});
}
});
Me gusta la simplicidad de la map
, pero el manejo de errores de flatmap
(no la verbosidad). No he visto ninguna mejor práctica sobre esto flotando y tengo curiosidad por saber cómo se usa esto en la práctica.
subscriber.onError()
etc. Todos los ejemplos que he visto tienen errores enrutados de esa manera. ¿Eso no importa?OnErrorThrowable
areprivate
y que necesita usarOnErrorThrowable.from(e)
en su lugar.FlatMap se comporta de manera muy similar a map, la diferencia es que la función que aplica devuelve un observable en sí mismo, por lo que es perfectamente adecuada para mapear sobre operaciones asincrónicas.
En el sentido práctico, la función que aplica Map simplemente hace una transformación sobre la respuesta encadenada (no devuelve un Observable); mientras que la función FlatMap aplica devuelve un
Observable<T>
, por eso se recomienda FlatMap si planea hacer una llamada asincrónica dentro del método.Resumen:
Un claro ejemplo se puede ver aquí: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk .
Couchbase Java 2.X Client utiliza Rx para proporcionar llamadas asíncronas de una manera conveniente. Como utiliza Rx, tiene los métodos map y FlatMap, la explicación en su documentación puede ser útil para comprender el concepto general.
Para manejar errores, anule onError en su suscriptor.
Puede ser útil mirar este documento: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
Puede encontrar una buena fuente sobre cómo administrar los errores con RX en: https://gist.github.com/daschl/db9fcc9d2b932115b679
fuente
En su caso, necesita un mapa, ya que solo hay 1 entrada y 1 salida.
La función suministrada por map simplemente acepta un artículo y devuelve un artículo que se emitirá más (solo una vez) hacia abajo.
La función proporcionada por flatMap acepta un elemento y luego devuelve un "Observable", lo que significa que cada elemento del nuevo "Observable" se emitirá por separado más abajo.
Puede que el código te aclare las cosas:
Salida:
fuente
La forma en que lo pienso es que usas
flatMap
cuando la función que querías poner dentro demap()
devuelve unObservable
. En cuyo caso aún podría intentar usarlo,map()
pero no sería práctico. Déjame intentar explicar por qué.Si en ese caso decidieras seguir
map
, obtendrías unObservable<Observable<Something>>
. Por ejemplo, en su caso, si utilizamos una biblioteca RxGson imaginaria, que ha devuelto unObservable<String>
de ella deltoJson()
método (en lugar de simplemente devolver unaString
) que se vería así:En este punto, sería bastante complicado para
subscribe()
un observable. Dentro de él, obtendría un valorObservable<String>
al que volvería a necesitarsubscribe()
. Lo cual no es práctico ni agradable de ver.Entonces, para que sea útil, una idea es "aplanar" este observable de observables (puede comenzar a ver de dónde viene el nombre _flat_Map). RxJava proporciona algunas formas de aplanar los observables y, por simplicidad, supongamos que fusionar es lo que queremos. Merge básicamente toma un montón de observables y emite cada vez que alguno de ellos emite. (Mucha gente argumentaría que cambiar sería un valor predeterminado mejor. Pero si está emitiendo un solo valor, no importa de todos modos).
Entonces, modificando nuestro fragmento anterior obtendríamos:
Esto es mucho más útil, porque suscribiéndote a eso (o mapeando, filtrando o ...) solo obtienes el
String
valor. (Además,merge()
ten en cuenta que tal variante de no existe en RxJava, pero si entiendes la idea de la fusión, espero que también entiendas cómo funcionaría eso).Básicamente, porque
merge()
probablemente esto solo debería ser útil cuando se logra unmap()
retorno de un observable y para que no tenga que escribir esto una y otra vez,flatMap()
se creó como una abreviatura. Aplica la función de mapeo como lomap()
haría normalmente , pero luego, en lugar de emitir los valores devueltos, también los "aplana" (o fusiona).Ese es el caso de uso general. Es más útil en una base de código que usa Rx en todo el lugar y tiene muchos métodos que devuelven observables, que desea encadenar con otros métodos que devuelven observables.
En su caso de uso, también resulta útil, ya
map()
que solo puede transformar un valor emitido enonNext()
otro valor emitido enonNext()
. Pero no puede transformarlo en múltiples valores, ningún valor o un error. Y como akarnokd escribió en su respuesta (y ten en cuenta que es mucho más inteligente que yo, probablemente en general, pero al menos cuando se trata de RxJava) no deberías lanzar excepciones de tu partemap()
. Entonces, en su lugar, puede usarflatMap()
ycuando todo va bien, pero
cuando algo falla
Consulte su respuesta para obtener un fragmento completo: https://stackoverflow.com/a/30330772/1402641
fuente
La pregunta es ¿ Cuándo usas map vs flatMap en RxJava? . Y creo que una demostración simple es más específica.
Cuando desee convertir un elemento emitido a otro tipo, en su caso, la conversión de archivos a String, map y flatMap puede funcionar. Pero prefiero el operador de mapa porque es más claro.
Sin embargo, en algún lugar,
flatMap
puede hacer un trabajo mágico peromap
no puede. Por ejemplo, quiero obtener la información de un usuario, pero primero tengo que obtener su identificación cuando el usuario inicia sesión. Obviamente necesito dos solicitudes y están en orden.Vamos a empezar.
Aquí hay dos métodos, uno para el inicio de sesión devuelto
Response
y otro para obtener información del usuario.Como puede ver, en la función se aplica flatMap, al principio obtengo la identificación del
Response
usuario y luego obtengo la información del usuario. Cuando finalizan dos solicitudes, podemos hacer nuestro trabajo, como actualizar la IU o guardar datos en la base de datos.Sin embargo, si lo usa,
map
no puede escribir un código tan agradable. En una palabra,flatMap
puede ayudarnos a serializar solicitudes.fuente
Aquí es un simple regla básica que utilizo como ayuda a decidir cuándo usar
flatMap()
másmap()
de Rx deObservable
.Una vez que decida que va a emplear una
map
transformación, escribiría su código de transformación para devolver algún Objeto, ¿verdad?Si lo que está devolviendo como resultado final de su transformación es:
un objeto no observable que usarías solo
map()
. Ymap()
envuelve ese objeto en un Observable y lo emite.un
Observable
objeto, entonces lo usaríasflatMap()
. YflatMap()
desenvuelve el Observable, recoge el objeto devuelto, lo envuelve con su propio Observable y lo emite.Digamos, por ejemplo, que tenemos un método titleCase (String inputParam) que devuelve el objeto Titled Cased String del parámetro de entrada. El tipo de retorno de este método puede ser
String
oObservable<String>
.Si el tipo de devolución
titleCase(..)
fuera simpleString
, entonces usaríasmap(s -> titleCase(s))
Si el tipo de retorno
titleCase(..)
fueraObservable<String>
, entonces usaríasflatMap(s -> titleCase(s))
Espero que eso aclare.
fuente
Solo quería agregar eso
flatMap
, realmente no necesita usar su propio Observable personalizado dentro de la función y puede confiar en los métodos / operadores estándar de fábrica:En general, debe evitar lanzar excepciones (Runtime-) de los métodos onXXX y las devoluciones de llamada si es posible, a pesar de que pusimos tantas salvaguardas como pudimos en RxJava.
fuente
En ese escenario, use el mapa, no necesita un nuevo Observable para él.
debe usar Exceptions.propagate, que es un contenedor para poder enviar esas excepciones marcadas al mecanismo de rx
Entonces deberías manejar este error en el suscriptor
Hay una excelente publicación para ello: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
fuente
En algunos casos, podría terminar teniendo una cadena de observables, en donde su observable devolvería otro observable. 'flatmap' desenvuelve el segundo observable que está enterrado en el primero y le permite acceder directamente a los datos que el segundo observable está escupiendo mientras se suscribe.
fuente
Mapa plano asigna observables a observables. Mapa asigna elementos a elementos.
Flatmap es más flexible, pero Map es más ligero y directo, por lo que depende un poco de su caso de uso.
Si está haciendo CUALQUIER COSA asíncrona (incluido el cambio de hilos), debe usar Flatmap, ya que Map no verificará si el consumidor está dispuesto (parte de la ligereza)
fuente