reactor与反应堆中的flatMap

时间:2018-03-05 16:31:23

标签: java project-reactor

我找到了很多关于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,反之亦然,一切仍然有效。我想知道区别是什么。

2 个答案:

答案 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>。它知道如何处理它:订阅它并在输出序列中传播它的元素。因此,返回类型为TFlux<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);
      });
  }
                                           
}