web-dev-qa-db-ja.com

Mono.defer()は何をしますか?

いくつかのSpring webfluxコードでMono.defer()に遭遇しました

私はドキュメントでメソッドを調べましたが、説明がわかりません:

「サブスクライバーダウンストリームごとにサブスクライブするターゲットMonoを提供するMonoプロバイダーを作成します」

説明と例をお願いします。私が参照する可能性のある一連のReactorサンプルコード(そのユニットテスト?)がある場所はありますか?.

ありがとう

18
James Render

これは少し単純化しすぎていますが、概念的には、Reactorのソースは遅延または熱心です。 HTTPリクエストのようなより高度なものは、遅延評価されることが期待されています。反対に、_Mono.just_や_Flux.fromIterable_のような最も単純なものは熱心です。

つまり、Mono.just(System.currentTimeMillis())を呼び出すと、すぐにcurrentTimeMillis()メソッドが呼び出され、結果がキャプチャされます。上記の結果は、サブスクライブされた後のMonoによるemittedのみです。複数回登録しても値は変わりません。

_Mono<Long> clock = Mono.just(System.currentTimeMillis());
//time == t0

Thread.sleep(10_000);
//time == t10
clock.block(); //we use block for demonstration purposes, returns t0

Thread.sleep(7_000);
//time == t17
clock.block(); //we re-subscribe to clock, still returns t0
_

defer演算子は、このソースを遅延させるためにあり、ラムダのコンテンツを再評価します新しいサブスクライバーが存在するたびに

_Mono<Long> clock = Mono.defer(() -> Mono.just(System.currentTimeMillis()));
//time == t0

Thread.sleep(10_000);
//time == t10
clock.block(); //invoked currentTimeMillis() here and returns t10

Thread.sleep(7_000);
//time == t17
clock.block(); //invoke currentTimeMillis() once again here and returns t17
_
37
Simon Baslé

簡単な言葉で言うと、最初のビューで見るとMono.just()のようですが、そうではありません。 Mono.just()を実行するとすぐにObservable(Mono)が作成されて再利用されますが、deferを使用するとすぐには作成されず、すべてのサブスクライブで新しいObservableが作成されます。

違いを確認するための1つの使用例

    int a = 5;
@Override
public void run(String... args) throws Exception {

    Mono<Integer> monoJust = Mono.just(a);
    Mono<Integer> monoDefer = Mono.defer(() -> Mono.just(a));

    monoJust.subscribe(integer1 -> System.out.println(integer1));
    monoDefer.subscribe(integer1 -> System.out.println(integer1));

    a = 7;
    monoJust.subscribe(integer1 -> System.out.println(integer1));
    monoDefer.subscribe(integer1 -> System.out.println(integer1));
}

印刷:5、5、5、7

mono.justがオブザーバブルをすぐに作成し、値が変更されても変更されないことがわかりますが、延期はサブスクライブでオブザーバブルを作成するため、現在のオンサブスクライブ値で作業します

9
Ricard Kollcaku

私は別のユースケースでdeferを試みていました。他の人を助けるかもしれないので、以下のコードを書いてチェックして共有してください。私の使用例は、2つのMonosをチェーンして、最初のものが完了してから2番目のものが使用されるようにすることでした。また、2番目のブロックには、Monoまたはempty応答でerrorに応答するために使用されるブロッキング呼び出しが含まれていました。 deferがないと、最初の呼び出しの結果に関係なく、ブロッキング呼び出しが実行されます。ただし、deferの使用中は、最初のMonoが完了したときにのみブロッキング呼び出しが実行されます。以下のコード:

public static void main(String[] args) {
    long cur = System.currentTimeMillis();
    boolean succeed = true;

    Mono<Integer> monoJust = Mono.create(consumer -> {
        System.out.println("MonoJust inside " + (System.currentTimeMillis() - cur));
        if (succeed) {
            consumer.success(1);
        } else {
            consumer.error(new RuntimeException("aaa"));
        }
    });

    Mono<String> monoJustStr = Mono.create(consumer -> {
        System.out.println("MonoJustStr inside: " + (System.currentTimeMillis() - cur));
        consumer.success("one");
    });

    System.out.println("##1##: Begin");
    monoJust.then(evaluator() ? Mono.empty() : monoJustStr).subscribe(d -> System.out.println("##1##: "+d), e-> System.err.println(e));
    System.out.println("##1##: Done: "+(System.currentTimeMillis() - cur));

    System.out.println("\n\n\n##2##: Begin");
    monoJust.then(Mono.defer(() -> evaluator() ? Mono.empty() : monoJustStr)).subscribe(d -> System.out.println("##2##: "+d), e-> System.err.println(e));
    System.out.println("##2##: Done: " + (System.currentTimeMillis() - cur));

}

private static boolean evaluator() {
    System.out.println("Inside Evaluator");
    return false;
}

succeed=trueを使用した出力-「Inside Evaluator」と「MonoJust inside」のシーケンスを確認します

##1##: Begin
Inside Evaluator
MonoJust inside 540
MonoJustStr inside: 542
##1##: one
##1##: Done: 542



##2##: Begin
MonoJust inside 544
Inside Evaluator
MonoJustStr inside: 544
##2##: one
##2##: Done: 544

以下はsucceed = falseの出力です。エバリュエーターが呼び出されないことに注意してください。

##1##: Begin
Inside Evaluator
MonoJust inside 565
Java.lang.RuntimeException: aaa
##1##: Done: 567



##2##: Begin
MonoJust inside 569
Java.lang.RuntimeException: aaa
##2##: Done: 569
0
Pavan Kumar