trituración
Encontré muchas respuestas con respecto a RxJava, pero quiero entender cómo funciona en Reactor.
Mi comprensión actual es muy vaga, tiendo a pensar que el mapa es sincrónico y flatMap es asincrónico, pero realmente no puedo entenderlo.
Aquí hay un ejemplo:
files.flatMap { it ->
Mono.just(Paths.get(UPLOAD_ROOT, it.filename()).toFile())
.map {destFile ->
destFile.createNewFile()
destFile
}
.flatMap(it::transferTo)
}.then()
tengo archivos (un Flux<FilePart>
) y quiero copiarlo a algunos UPLOAD_ROOT
en el servidor
Este ejemplo está tomado de un libro.
Puedo cambiar todo el .map
a .flatMap
y viceversa y todo sigue funcionando. Me pregunto cuál es la diferencia.
simon basle
map
es para transformaciones síncronas, sin bloqueo, 1 a 1flatMap
es para transformaciones asíncronas (sin bloqueo) de 1 a N
La diferencia es visible en la firma del método:
map
toma unaFunction<T, U>
y devuelve unFlux<U>
flatMap
toma unaFunction<T, Publisher<V>>
y devuelve unFlux<V>
Esa es la pista principal: tú poder pasar un Function<T, Publisher<V>>
a un map
pero no sabría qué hacer con el Publishers
y eso daría como resultado una Flux<Publisher<V>>
una secuencia de editores inertes.
Por otro lado, flatMap
espera un Publisher<V>
para cada T
. Sabe qué hacer con él: suscribirse y propagar sus elementos en la secuencia de salida. Como resultado, el tipo de retorno es Flux<V>
: flatMap
aplanará cada interior Publisher<V>
en la secuencia de salida de todo el V
s.
Sobre el aspecto 1-N:
para cada <T>
elemento de entrada, flatMap
lo asigna a un Publisher<V>
. En algunos casos (por ejemplo, una solicitud HTTP), ese editor emitirá solo un elemento, en cuyo caso estamos bastante cerca de un asíncrono map
.
Pero ese es el caso degenerado. El caso genérico es que un Publisher
puede emitir múltiples elementos, y flatMap
funciona igual de bien.
Por ejemplo, imagina que tienes una base de datos reactiva y usas FlatMap a partir de una secuencia de ID de usuario, con una solicitud que devuelve un conjunto de usuarios de Badge
. Terminas con un solo Flux<Badge>
de todas las insignias de todos estos usuarios.
Es map
realmente sincronizado y sin bloqueo?
Sí: es síncrono en la forma en que el operador lo aplica (una llamada de método simple y luego el operador emite el resultado) y no bloquea en el sentido de que la función en sí no debería bloquear al operador que la llama. En otros términos, no debería introducir latencia. Eso es porque un Flux
sigue siendo asincrónico en su conjunto. Si bloquea la mitad de la secuencia, afectará al resto de la Flux
procesamiento, o incluso otros Flux
.
Si su función de mapa está bloqueando/introduce latencia pero no se puede convertir para devolver un Publisher
considerar publishOn
/subscribeOn
para compensar ese trabajo de bloqueo en un hilo separado.
-
quisiste decir mapa es
blocking
, ¿bien? yo tampoco entiendo el1-to-N
en realidad. ¿Puede dar un ejemplo cuando uno es útil sobre el otro? Entiendo que uso flatMap cuando espero que el resultado sea asíncrono, porque aplana a los editores una vez que el resultado está allí, ¿es correcto?– trituración
8 de marzo de 2018 a las 10:31
-
no, la función de mapa no debe bloquear (a menos que también compense el trabajo en un hilo separado usando
publishOn
/subscribeOn
). es decir, se ejecuta sincrónicamente pero no debe tener latencia. La función flatMap es asíncrona y, de hecho, el operador aplana los resultados a medida que están disponibles.– Simón Basle
8 de marzo de 2018 a las 11:34
-
edité la respuesta para explicar estos dos aspectos + ejemplo flatMap 1-N
– Simón Basle
8 de marzo de 2018 a las 11:45
-
¿Esta respuesta está desactualizada? Creo que lo que llamas flatMap ahora es “flatMapMany”, y flatMap hace algo diferente: github.com/reactor/reactor-core/issues/516
– Hazel T.
16 de noviembre de 2018 a las 22:07
-
Solo que Mono ahora tiene la sutileza adicional de tener flatMap (transformación asíncrona 1 a 1) y flatMapMany (asíncrono 1 a n)
– Simón Basle
17 de noviembre de 2018 a las 14:51
Raouf Makhlouf
El método flatMap es similar al método de mapa con la diferencia clave de que el proveedor que le proporcione debe devolver un Mono<T>
o Flux<T>
.
Usar el método del mapa daría como resultado una Mono<Mono<T>>
mientras que usar flatMap da como resultado un Mono<T>
.
Por ejemplo, es útil cuando tienes que hacer una llamada de red para recuperar datos, con una API de Java que devuelve un Mono, y luego otra llamada de red que necesita el resultado de la primera.
// Signature of the HttpClient.get method
Mono<JsonObject> get(String url);
// The two urls to call
String firstUserUrl = "my-api/first-user";
String userDetailsUrl = "my-api/users/details/"; // needs the id at the end
// Example with map
Mono<Mono<JsonObject>> result = HttpClient.get(firstUserUrl).
map(user -> HttpClient.get(userDetailsUrl + user.getId()));
// This results with a Mono<Mono<...>> because HttpClient.get(...)
// returns a Mono
// Same example with flatMap
Mono<JsonObject> bestResult = HttpClient.get(firstUserUrl).
flatMap(user -> HttpClient.get(userDetailsUrl + user.getId()));
// Now the result has the type we expected
Además, permite manejar los errores con precisión:
public UserApi {
private HttpClient httpClient;
Mono<User> findUser(String username) {
String queryUrl = "http://my-api-address/users/" + username;
return Mono.fromCallable(() -> httpClient.get(queryUrl)).
flatMap(response -> {
if (response.statusCode == 404) return Mono.error(new NotFoundException("User " + username + " not found"));
else if (response.statusCode == 500) return Mono.error(new InternalServerErrorException());
else if (response.statusCode != 200) return Mono.error(new Exception("Unknown error calling my-api"));
return Mono.just(response.data);
});
}
}
Zahid Khan
Cómo funciona el mapa internamente en el Reactor.
Creando un Player
clase.
@Data
@AllArgsConstructor
public class Player {
String name;
String name;
}
Ahora creando algunas instancias de Player
clase
Flux<Player> players = Flux.just(
"Zahid Khan",
"Arif Khan",
"Obaid Sheikh")
.map(fullname -> {
String[] split = fullname.split("\\s");
return new Player(split[0], split[1]);
});
StepVerifier.create(players)
.expectNext(new Player("Zahid", "Khan"))
.expectNext(new Player("Arif", "Khan"))
.expectNext(new Player("Obaid", "Sheikh"))
.verifyComplete();
Lo que es importante entender sobre map() es que el mapeo se realiza de forma síncrona, ya que Flux de origen publica cada elemento. Si desea realizar la asignación de forma asíncrona, debe considerar la operación flatMap().
Cómo funciona FlatMap internamente.
Flux<Player> players = Flux.just(
"Zahid Khan",
"Arif Khan",
"Obaid Sheikh")
.flatMap(
fullname ->
Mono.just(fullname).map(p -> {
String[] split = p.split("\\s");
return new Player(split[0], split[1]);
}).subscribeOn(Scheduler.parallel()));
List<Player> playerList = Arrays.asList(
new Player("Zahid", "Khan"),
new Player("Arif", "Khan"),
new Player("Obaid", "Sheikh"));
StepVerifier.create(players).expectNextMatches(player ->
playerList.contains(player))
.expectNextMatches(player ->
playerList.contains(player))
.expectNextMatches(player ->
playerList.contains(player))
.expectNextMatches(player ->
playerList.contains(player))
.verifyComplete();
Internamente, en Flatmap(), se realiza una operación map() en Mono para transformar String en Player. Además, subcribeOn () indica que cada suscripción debe realizarse en un hilo paralelo. En ausencia de subscribeOn() flatmap() actúa como sincronizado.
El mapa es para transformaciones uno a uno síncronas, sin bloqueo, mientras que flatMap es para transformaciones uno a muchos asíncronas (sin bloqueo).
-
Basado en la imagen En su ejemplo de mapa plano, es posible tener una cantidad diferente de elementos de entrada y salida. ¿Podría dar un ejemplo? porque en su ejemplo tiene la misma cantidad (3 elementos de entrada y 3 elementos de salida).
– desbordamiento de gstack
10 de enero a las 8:10
¿Puede ser más específico acerca de su pregunta real? A qué métodos te refieres en realidad, hay múltiples
map
yflatMap
métodos en Java.– Zabuzard
5 de marzo de 2018 a las 16:43
Estoy hablando del proyecto Reactor. projectreactor.io/docs/core/release/api/reactor/core/publisher/…
– trituración
5 de marzo de 2018 a las 16:48
Sí, ahora por favor sea más específico sobre la pregunta. ¿Dónde exactamente tienes dificultades para entenderlo? ¿Por qué echar un vistazo al código fuente y la documentación no aclara la confusión? ¿Hay algo específico que no entiendes?
– Zabuzard
5 de marzo de 2018 a las 16:52
Entiendo por los documentos que ambas son formas de iterar sobre un Flux dado y que
map
es sincronizado yflatMap
no es. Pero también entiendo que la función que doy enmap
se ejecuta asíncrono y no sé cuándo usar cuál.– trituración
5 de marzo de 2018 a las 16:56
… Voy a actualizar con un ejemplo concreto.
– trituración
5 de marzo de 2018 a las 16:57