What Does Mono.defer() Do? – Mono.defer()是做什么的?

最后修改: 2021年 6月 21日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In Reactive Programming, there are many ways we can create a publisher of type Mono or Flux. Here, we’ll look at the use of the defer method to delay the execution of a Mono publisher.

反应式编程中,我们有许多方法可以创建MonoFlux类型的发布器。在这里,我们将看看如何使用defer方法来延迟Mono发布器的执行。

2. What Is the Mono.defer Method?

2.什么是Mono.defer方法?

We can create a cold publisher which can produce at most one value using defer method of the Mono. Let’s look at the method signature:

我们可以使用defer Mono 的方法创建一个最多可以产生一个值的冷发布器。让我们来看看这个方法的签名。

public static <T> Mono<T> defer(Supplier<? extends Mono<? extends T>> supplier)

Here, defer takes in a Supplier of Mono publisher and returns that Mono lazily when subscribed downstream.

这里,defer接收了Mono发布者的Supplier,并在下游订阅时懒洋洋地返回该Mono

However, the question is, what is a cold publisher or a lazy publisher? Let’s look into that.

然而,问题是,什么是冷酷的出版商或懒惰的出版商?让我们来研究一下。

Executing thread evaluates cold publishers only when consumers subscribe to them. While the hot publisher evaluated eagerly before any subscription. We have the method Mono.just() that gives a hot publisher of type Mono.

执行线程只有在消费者订阅时才会评估冷发布者。 而热发布者在任何订阅之前都会急切地评估。我们有方法Mono.just(),它给出了一个类型为Mono的热发布者。

3. How Does It Work?

3.它是如何工作的?

Let’s explore a sample use case having Supplier of type Mono:

让我们探索一个具有Supplier类型Mono的示例用例。

private Mono<String> sampleMsg(String str) {
    log.debug("Call to Retrieve Sample Message!! --> {} at: {}", str, System.currentTimeMillis());
    return Mono.just(str);
}

Here, this method returns a hot Mono publisher. Let’s subscribe to this eagerly:

在这里,这个方法返回一个热的Mono发布器。让我们急切地订阅这个。

public void whenUsingMonoJust_thenEagerEvaluation() throws InterruptedException {

    Mono<String> msg = sampleMsg("Eager Publisher");

    log.debug("Intermediate Test Message....");

    StepVerifier.create(msg)
      .expectNext("Eager Publisher")
      .verifyComplete();

    Thread.sleep(5000);

    StepVerifier.create(msg)
      .expectNext("Eager Publisher")
      .verifyComplete();
}

On execution, we can see the following in logs:

执行时,我们可以在日志中看到以下内容。

20:44:30.250 [main] DEBUG com.baeldung.mono.MonoUnitTest - Call to Retrieve Sample Message!! --> Eager Publisher at: 1622819670247
20:44:30.365 [main] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework
20:44:30.365 [main] DEBUG com.baeldung.mono.MonoUnitTest - Intermediate Test Message....

Here, we can notice that:

在这里,我们可以注意到。

  • According to the instructions sequence, the main thread eagerly executes the method sampleMsg.
  • On both subscriptions using StepVerifier, the main thread uses the same output of sampleMsg. Therefore, no new evaluation.

Let’s see how Mono.defer() converts it to a cold (lazy) publisher:

让我们看看Mono.defer()如何将其转换为一个冷(懒)发布器。

public void whenUsingMonoDefer_thenLazyEvaluation() throws InterruptedException {

    Mono<String> deferMsg = Mono.defer(() -> sampleMsg("Lazy Publisher"));

    log.debug("Intermediate Test Message....");

    StepVerifier.create(deferMsg)
      .expectNext("Lazy Publisher")
      .verifyComplete();

    Thread.sleep(5000);

    StepVerifier.create(deferMsg)
      .expectNext("Lazy Publisher")
      .verifyComplete();

}

On executing this method, we can see the following logs in the console:

在执行这个方法时,我们可以在控制台看到以下日志。

20:01:05.149 [main] DEBUG com.baeldung.mono.MonoUnitTest - Intermediate Test Message....
20:01:05.187 [main] DEBUG com.baeldung.mono.MonoUnitTest - Call to Retrieve Sample Message!! --> Lazy Publisher at: 1622817065187
20:01:10.197 [main] DEBUG com.baeldung.mono.MonoUnitTest - Call to Retrieve Sample Message!! --> Lazy Publisher at: 1622817070197

Here, we can notice few points from the log sequence:

在这里,我们可以从日志序列中注意到几个要点。

  • StepVerifier executes the method sampleMsg on each subscription, instead of when we defined it.
  • After a delay of 5 seconds, the second consumer subscribing to the method sampleMsg executes it again.

This is how the defer method turns hot into a cold publisher.

这就是defer方法如何将热的变成冷的出版商。

4. Use Cases for Mono.defer?

4.Mono.defer的用例?

Let’s look at the possible use cases where we can use Mono.defer() method:

让我们看看我们可以使用Mono.defer()方法的可能用例。

  • When we have to conditionally subscribe to a publisher
  • When each subscribed execution could produce a different result
  • deferContextual can be used for the current context-based evaluation of publisher

4.1. Sample Usage

4.1.使用样本

Let’s go through one sample that is using the conditional Mono.defer() method:

让我们来看看一个使用条件性Mono.defer()方法的例子。

public void whenEmptyList_thenMonoDeferExecuted() {

    Mono<List<String>> emptyList = Mono.defer(() -> monoOfEmptyList());

    //Empty list, hence Mono publisher in switchIfEmpty executed after condition evaluation
    Flux<String> emptyListElements = emptyList.flatMapIterable(l -> l)
      .switchIfEmpty(Mono.defer(() -> sampleMsg("EmptyList")))
      .log();

    StepVerifier.create(emptyListElements)
      .expectNext("EmptyList")
      .verifyComplete();
}

Here, the supplier of Mono publisher sampleMsg is placed in switchIfEmpty method for conditional execution. Hence, sampleMsg executed only when it is subscribed lazily.

在这里,Mono发布者sampleMsgsupplier被放在switchIfEmpty方法中进行有条件执行。因此,sampleMsg只在被懒惰地订阅时执行。

Now, let’s look at the same code for the non-empty list:

现在,让我们看一下非空列表的相同代码。

public void whenNonEmptyList_thenMonoDeferNotExecuted() {

    Mono<List<String>> nonEmptyist = Mono.defer(() -> monoOfList());

    //Non empty list, hence Mono publisher in switchIfEmpty won't evaluated.
    Flux<String> listElements = nonEmptyist.flatMapIterable(l -> l)
      .switchIfEmpty(Mono.defer(() -> sampleMsg("NonEmptyList")))
      .log();

    StepVerifier.create(listElements)
      .expectNext("one", "two", "three", "four")
      .verifyComplete();
}

Here, the sampleMsg isn’t executed because it isn’t subscribed.

这里,sampleMsg没有被执行,因为它没有被订阅。

5. Conclusion

5.总结

In this article, we discussed Mono.defer() method and hot/cold publishers. In addition, how we can convert a hot publisher to a cold publisher. Finally, we also discussed its working with sample use cases.

在这篇文章中,我们讨论了Mono.defer()方法和热/冷发布器。此外,我们如何将一个热发布器转换为冷发布器。最后,我们还讨论了它与样本用例的工作。

As always, the code example is available over on GitHub.

像往常一样,代码示例可在GitHub上获得