Reactor内嵌Mono未执行原因及解决方案咨询
问题:Reactor中内嵌Mono未执行,如何确保批量操作完成后执行后续逻辑?
问题代码
deleteCommentReply方法
private Mono<Void> deleteCommentReply(String id) { System.out.println("YES"); return this.getCommentById(id).flatMap(comment -> { System.out.println("YES1"); // 执行其他Mono调用后返回Mono.empty(); return Mono.empty(); }); }
注:
getCommentById返回Mono,未找到时会抛出异常,但ID存在时方法本身无问题。
调用代码
List<Mono<Void>> monos = new ArrayList<>(); for (String s : comment.getReplyCommentIds()) { monos.add(deleteCommentReply(s)); } Flux<Mono<Void>> deleteReply = Flux.fromIterable(monos); return deleteReply.then(finalDelete(id, comment));
问题现象
执行时YES打印次数与回复ID数量一致,但YES1从未打印。若在then前调用.blockLast(),则能成功执行一次(YES和YES1均打印,数据也从数据库删除),但无法批量执行。需要解决两个问题:
- 为何内嵌Mono未执行?
- 如何正确调用,确保
finalDelete()在所有删除操作完成后执行?
原因分析
你创建的Flux<Mono<Void>>只是一个包含Mono对象的容器流,并没有触发这些内部Mono的订阅执行。Reactor遵循懒加载原则:只有当流被订阅时,整个链路的逻辑才会启动。deleteReply.then(...)只会等待这个容器流遍历完所有Mono对象,不会主动订阅每个内部Mono,因此getCommentById对应的逻辑完全没被触发,自然不会打印YES1。
而.blockLast()是阻塞式订阅操作,它会强制触发流的执行,但只会等待最后一个元素完成,所以你只能看到一次YES1打印,且阻塞操作不符合响应式编程的设计原则,应避免使用。
修复方案
需要将容器流转换成真正执行所有Mono的响应式流,以下是两种常用的正确实现方式:
方式1:使用flatMap平铺执行
通过flatMap把每个Mono对象转换成流的一部分,触发内部逻辑的执行:
List<Mono<Void>> monos = new ArrayList<>(); for (String s : comment.getReplyCommentIds()) { monos.add(deleteCommentReply(s)); } // 平铺内部Mono,触发所有删除逻辑执行 Flux<Void> deleteReply = Flux.fromIterable(monos) .flatMap(mono -> mono); // then会等待所有flatMap中的操作完成后,再执行finalDelete return deleteReply.then(finalDelete(id, comment));
方式2:使用Flux.merge合并执行(更简洁)
直接将Mono列表合并成一个Flux,自动触发所有内部Mono的执行:
// 直接合并所有删除操作的Mono,批量执行 Flux<Void> deleteReply = Flux.merge(comment.getReplyCommentIds() .stream() .map(this::deleteCommentReply) .toList()); return deleteReply.then(finalDelete(id, comment));
额外说明
- 如果需要保证删除操作按顺序执行(而非并行),可以把
flatMap换成concatMap,或用Flux.concat替代Flux.merge。 then操作符的特性就是等待前面的流完全完成后,才会订阅并执行后续的finalDelete返回的Mono,完全符合“所有操作完成后执行收尾逻辑”的需求。
内容的提问来源于stack exchange,提问作者cooooool
相关产品推荐
相关产品推荐

