block()、subscribe()、subscribe(-) 之间的区别是什么?

14
Mono.delay(Duration.ofMillis(10)).map(d -> {
            System.out.println(d);
            return d;
        }).block();

输出:0

当我使用subscribe()subscribe(-)方法而不是block()方法调用时,无法在控制台上看到任何输出。

Mono.delay(Duration.ofMillis(10)).map(d -> {
        System.out.println(d);
        return d;
    }).subscribe(System.out::println);

我们需要在 Mono.delay(-) 方法之后仅使用doOnSubscribe (-)吗?
 Mono.delay(Duration.ofMillis(10)).doOnSubscribe(s -> {
        System.out.println("its printing doOnSubscribe");
    }).map(d -> {
        System.out.println(d);
        return d;
    }).subscribe(System.out::println);

输出它正在打印doOnSubscribe

1个回答

28

你的block()调用会显式地阻塞主线程,直到发布者完成。当它完成时,它将执行map()调用,因此打印该值。

另一方面,你的subscribe()调用异步地在单独的调度程序上执行Mono,使主线程离开以完成其他任务。由于默认使用守护进程线程执行订阅,因此程序不会在终止之前等待其完成。

如果你引入足够长的延迟时间使Mono完成,就会看到期望的结果:

Mono.delay(Duration.ofMillis(10)).map(d -> {
    System.out.println(d);
    return d;
}).subscribe(System.out::println);

Thread.currentThread().sleep(500);

0 接着被输出两次,一次是由于 map() 方法调用,另一次是由于使用了 System.out::println 作为消费者。

在实际应用中,显然不会随意添加 sleep() 调用 - CountDownLatch 是一个更明智的选择:

CountDownLatch cdl = new CountDownLatch(1);
Mono.delay(Duration.ofMillis(10))
        .map(d -> {
            System.out.println(d);
            return d;
        })
        .doOnTerminate(() -> cdl.countDown())
        .subscribe(System.out::println);
cdl.await();

非常感谢 @michael-berry。对我的需求非常有用。 - G SriHAri
4
在这种情况下,CountDownLatch解决方案相当于调用简单的block(),除非我们明确指定错误处理程序,否则在上述代码中错误会丢失,在block()的情况下,反应器会自动抛出错误。 - Martin Tarjányi
嗨,Michael,subscribe()会在哪个线程中执行我的代码?我的意思是弹性/有界弹性等等? - SoT
1
在这种特定情况下,我猜确实是这样。但是,在我有多个订阅并且需要确保它们全部完成后才能继续的情况下,了解CountDownLatch对我非常有帮助。而且我不能只是阻塞,否则它会在并行启动其他订阅之前在第一个订阅上阻塞。 - Vinícius M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接