我找到了很多关于RxJava的答案,但我想了解它在Reactor中的作用。
我目前的理解非常模糊,我倾向于认为地图是同步的,而flatMap是异步的,但我不能真正了解它。
以下是一个例子:
files.flatMap { it ->
Mono.just(Paths.get(UPLOAD_ROOT, it.filename()).toFile())
.map {destFile ->
destFile.createNewFile()
destFile
}
.flatMap(it::transferTo)
}.then()
我有文件(Flux<FilePart>
),我想将其复制到服务器上的某些UPLOAD_ROOT
。
这个例子来自一本书。
我可以将所有.map
更改为.flatMap
,反之亦然,一切仍然有效。我想知道区别是什么。
答案 0 :(得分:20)
map
用于同步,非阻塞,1对1转换flatMap
用于异步(非阻塞)1对N转换差异在方法签名中可见:
map
需要Function<T, U>
并返回Flux<U>
flatMap
需要Function<T, Publisher<V>>
并返回Flux<V>
这是主要提示:您可以将Function<T, Publisher<V>>
传递给map
,但它不知道如何处理{ {1}},这将导致Publishers
,一系列惰性发布者。
另一方面,Flux<Publisher<V>>
预计每个flatMap
都会Publisher<V>
。它知道如何处理它:订阅它并在输出序列中传播它的元素。因此,返回类型为T
:Flux<V>
会将每个内部flatMap
展平为所有 Publisher<V>
s的输出序列。< / p>
关于1-N方面:
对于每个V
输入元素,<T>
将其映射到flatMap
。在某些情况下(例如,HTTP请求),该发布者只会发出一个项目,在这种情况下,我们非常接近异步Publisher<V>
。
但那是堕落的情况。一般情况是map
可以发出多个元素,而Publisher
也能正常工作。
例如,假设您有一个被动数据库,并且您从一系列用户ID中获得flatMap,并返回一组用户flatMap
的请求。您最终得到所有这些用户的所有徽章中的Badge
个。
Flux<Badge>
是否真正同步且非阻止?
是:它是操作符应用它的方式是同步的(一个简单的方法调用,然后运算符发出结果)和非阻塞,因为函数本身不应该阻止操作符调用它。换句话说,它不应该引入延迟。这是因为map
作为一个整体仍然是异步的。如果它阻止中间序列,则会影响Flux
处理的其余部分,甚至其他Flux
。
如果你的map函数阻塞/引入延迟但无法转换为返回Flux
,请考虑Publisher
/ publishOn
来抵消单独线程上的阻塞工作。
答案 1 :(得分:2)
flatMap 方法类似于 map 方法,主要区别在于您提供给它的供应商应该返回 Mono<T>
或 Flux<T>
。
使用 map 方法会导致 Mono<Mono<T>>
而使用 flatMap 会导致 Mono<T>
。
例如,当您必须进行网络调用以检索数据时,它很有用,使用返回 Mono 的 Java API,然后是另一个需要第一个结果的网络调用。
// 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
此外,它还允许精确处理错误:
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);
});
}
}