Spring Webflux中如何等待List<Mono>完成后执行后续异步调用?
解决Reactor中等待所有异步调用完成后再执行后续逻辑的问题
你的问题在Spring WebFlux(Reactor)的异步开发里很常见——你希望先让所有API2的并发调用执行完毕,再触发API3的请求,达到类似JavaScript中async/await配合Promise.all的效果。原代码的问题出在使用了subscribe():这个方法会直接触发异步执行,但不会让当前的Reactor流等待它完成,导致API3会在API2的结果还没处理完时就提前启动。
核心修正思路
- 放弃用
subscribe()触发API2调用,而是把每个API2请求封装成Mono<Covers>,收集为一个List<Mono<Covers>> - 利用Reactor的组合操作符(比如
Mono.when())等待所有API2的Mono任务完成,统一获取结果 - 等所有API2结果处理完毕后,再链式调用API3,最后组装并返回
Artist对象
修正后的完整代码
import java.util.List; import java.util.stream.Collectors; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public Mono<Artist> getArtistInfo(String id) { // 调用API1获取基础音乐数据 Mono<MusicResponse> musicResponseMono = webClientBuilder .build() .get() .uri(uri + "\"" + id) .retrieve() .bodyToMono(MusicResponse.class); return musicResponseMono.flatMap(musicRes -> { Artist artist = new Artist(); List<Albums> albums = musicRes.getAlbums(); // 将每个专辑转换为API2的异步请求,收集为Mono列表 List<Mono<Covers>> coverMonos = albums.stream() .map(album -> webClientBuilder .build() .get() .uri("API 2 URL") // 这里记得替换为实际API2地址,可传入专辑相关参数如album.getId() .retrieve() .bodyToMono(Covers.class)) .collect(Collectors.toList()); // 等待所有API2请求完成,处理结果后再调用API3 return Mono.when(coverMonos) // 取出所有API2的返回结果 .thenMany(Flux.fromIterable(coverMonos)) .flatMap(mono -> mono) // 将封面图片添加到Artist对象 .doOnNext(cover -> artist.getAlbums().add(cover.getImage())) // 所有API2完成后,执行API3请求 .then(webClientBuilder .build() .get() .uri("API3 URL") // 替换为实际API3地址 .retrieve() .bodyToMono(Profiles.class)) // 处理API3结果,组装最终Artist并返回 .map(profileRes -> { artist.setDescription(profileRes.getDescription()); return artist; }); }); }
关键细节解释
替换
subscribe()为流内管理:
原代码的subscribe()会脱离当前的Reactor流上下文,无法被后续操作等待。我们把每个API2请求封装成Mono并收集,让Reactor统一管理这些异步任务的生命周期。Mono.when()的作用:Mono.when(coverMonos)会等待所有传入的Mono任务完成,返回一个Mono<Void>信号,表示所有API2请求已结束。之后我们通过thenMany取出每个Mono的实际结果进行处理。链式调用保证执行顺序:
整个逻辑是严格的流式链式执行:API1完成 → 启动所有API2并等待全部完成 → 把API2结果写入Artist→ 执行API3 → 返回最终组装好的Artist,完全避免了API3提前执行的问题。
额外优化建议
- 不要每次请求都调用
webClientBuilder.build(),建议在类中提前初始化并注入一个WebClient实例,减少重复创建对象的性能开销。 - 如果API2的URL需要用到专辑的参数(比如专辑ID),记得在
uri()中动态拼接,例如uri("api/albums/" + album.getId() + "/covers")。
内容的提问来源于stack exchange,提问作者Sourena Sahraian
相关产品推荐
相关产品推荐

