You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebFlux中Mono空场景处理:flatMap与delayUntil差异解析

WebFlux分支执行逻辑疑惑解答

基础代码定义

static Mono<String> init(String input) {
    if (input == null) {
        return Mono.empty();
    }

    if ("err".equals(input)) {
        throw new IllegalArgumentException("Some exception");
    }

    System.out.println("First Mono: " + input);
    return Mono.just("First Mono: " + input);
}

static Mono<Void> doesSomeStuff(String input) {
    System.out.println("Second Mono: " + input);
    return Mono.empty();
}

首次尝试(不符合预期)

/**
 * Wrong! The 3rd line should not be shown! Defer causes empty block to be lazily evaluated
 *
 * First Mono: Hello
 * Second Mono: First Mono: Hello
 * Second Mono: Empty
 */
Mono<String> firstMono = init("Hello");
firstMono.flatMap(WebfluxDemoApplication::doesSomeStuff)
        .switchIfEmpty(Mono.defer(() -> doesSomeStuff("Empty")))
        .block();

/**
 * Correct! Only the empty branch is executed
 *
 * Second Mono: Empty
 */
Mono<String> first2Mono = init(null);
first2Mono.flatMap(WebfluxDemoApplication::doesSomeStuff)
        .switchIfEmpty(Mono.defer(() -> doesSomeStuff("Empty")))
        .block();

正确实现(使用delayUntil+switchIfEmpty+then)

/**
 * Correct!
 *
 * First Mono: Hello
 * Second Mono: First Mono: Hello
 */
Mono<String> first5Mono = init("Hello");
first5Mono.delayUntil(initVal -> doesSomeStuff(initVal))
        .switchIfEmpty(Mono.defer(() -> doesSomeStuff("Empty")).cast(String.class))
        .then()
        .block();

System.out.println("---");

/**
 * Correct!
 *
 * Second Mono: Empty
 */
Mono<String> first6Mono = init(null);
first6Mono.delayUntil(initVal -> doesSomeStuff(initVal))
        .switchIfEmpty(Mono.defer(() -> doesSomeStuff("Empty")).cast(String.class))
        .then()
        .block();

System.out.println("---");

/**
 * Correct! It throws the Exception.
 *
 * Second Mono: Empty
 */
Mono<String> first7Mono = init("err");
first7Mono.delayUntil(initVal -> doesSomeStuff(initVal))
        .switchIfEmpty(Mono.defer(() -> doesSomeStuff("Empty")).cast(String.class))
        .then()
        .block();

问题解答

1. 为什么delayUntil可行而flatMap不行?

核心差异在于两者对上游信号的处理逻辑:

  • flatMap的问题:flatMap会将上游信号转换为新的Mono,这里doesSomeStuff返回Mono<Void>且内部是Mono.empty(),所以当上游init("Hello")非空时,flatMap最终输出的是一个空的Mono<Void>,这个空信号直接触发了后续的switchIfEmpty,导致回退任务被执行。
  • delayUntil的优势:delayUntil仅等待传入的Mono完成,但会完整保留上游的原始信号类型和值。上游init("Hello")返回的Mono<String>非空,执行完delayUntil里的任务后,依然向下游传递原始的非空信号,不会触发switchIfEmpty。

2. switchIfEmpty中为何需要cast操作?

switchIfEmpty要求传入的Mono类型必须与上游Mono完全一致:

  • 上游的first5Mono/first6Mono是Mono<String>,但doesSomeStuff("Empty")返回的是Mono<Void>,类型不匹配会导致编译错误。
  • .cast(String.class)是为了将Mono<Void>强制转换为Mono<String>,让类型检查通过。由于doesSomeStuff仅执行打印逻辑,实际不会传递任何值,这个转换不会引发运行时问题。

3. then的作用是否因doesSomeStuff返回Mono才生效?

不是,then的核心作用与上游Mono的类型无关:

  • then的本质是忽略上游所有的信号值,只等待整个序列执行完成,最终返回一个Mono<Void>。
  • 在当前场景中使用then,是为了统一最终的返回类型,同时确保整个链式调用的信号流完整执行完毕。即使doesSomeStuff返回其他类型的Mono,then依然能起到收尾、丢弃上游元素的作用。

内容的提问来源于stack exchange,提问作者user1927638

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:57:46