This seems to be one of the top searches for Reactor, at least when I type onErrorContinue in Google, onErrorResume pops up next to it. Let me paste my test code and some of my explanations below.

1 Basic functions

Here is a simple function that multiplies 5 consecutive numbers by 2, adds them, and throws an exception when i==2:

 public static void main(String... args) {
      Flux.range(1,5)
              .doOnNext(i -> System.out.println("input=" + i))
              .map(i -> i == 2 ? i / 0 : i)
              .map(i -> i * 2)
              .reduce((i,j) -> i+j)
              .doOnNext(i -> System.out.println("sum=" + i))
              .block();
  }

Apparently, the output is as follows:

 input=1
input=2
Exception in thread "main" java.lang.ArithmeticException: / by zero

2 only onErrorResume()

 public static void main(String... args) {
      Flux.range(1,5)
              .doOnNext(i -> System.out.println("input=" + i))
              .map(i -> i == 2 ? i / 0 : i)
              .map(i -> i * 2)
              .onErrorResume(err -> {
                  log.info("onErrorResume");
                  return Flux.empty();
              })
              .reduce((i,j) -> i+j)
              .doOnNext(i -> System.out.println("sum=" + i))
              .block();
  }

The output is as follows:

 input=1
input=2
17:40:47.828 [main] INFO com.example.demo.config.TestRunner - onErrorResume
sum=2

As the official documentation describes ( onErrorResume ), onErrorResume replaces Flux with the return content, so data after 2 will not be processed. The only thing worth mentioning is that onErrorResume() doesn't have to catch the exception right after the error. In the official website documentation of onErrorContinue() ( onErrorContinue ), only onErrorContinue() emphasizes the upstream keyword, but obviously, it has other meanings.

3 only onErrorContinue()

 public static void main(String... args) {
  Flux.range(1,5)
          .doOnNext(i -> System.out.println("input=" + i))
          .map(i -> i == 2 ? i / 0 : i)
          .map(i -> i * 2)
          .onErrorContinue((err, i) -> {log.info("onErrorContinue={}", i);})
          .reduce((i,j) -> i+j)
          .doOnNext(i -> System.out.println("sum=" + i))
          .block();
}

output:

 input=1
input=2
17:43:10.656 [main] INFO com.example.demo.config.TestRunner - onErrorContinue=2
input=3
input=4
input=5
sum=26

Apparently, onErrorContinue will discard the bad data 2, and continue with the number 3 until 5.

4 onErrorResume() then onErrorContinue()

 public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .map(i -> i == 2 ? i / 0 : i)
            .map(i -> i * 2)
            .onErrorResume(err -> {
                log.info("onErrorResume");
                return Flux.empty();
            })
            .onErrorContinue((err, i) -> {log.info("onErrorContinue={}", i);})
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

The output is the same as above:

 input=1
input=2
17:47:05.789 [main] INFO com.example.demo.config.TestRunner - onErrorContinue=2
input=3
input=4
input=5
sum=26

Did you expect such a result? onErrorContinue() will handle this error before onErrorResume() gets the error. It's obvious when two error handling functions are in the same function, but when your function has only onErrorResume() and some callers actually have onErrorContinue() , your onErrorResume() The reason why onErrorResume() is not being called may not be so obvious.

5 Use onErrorResume() to simulate onErrorContinue()

Some bloggers suggest we don't use onErrorContinue() at all, and only use onErrorResume() in all scenarios. But the above examples have shown that they produce different results. So how do we achieve it?

 public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .flatMap(i -> Mono.just(i)
                    .map(j -> j == 2 ? j / 0 : j)
                    .map(j -> j * 2)
                    .onErrorResume(err -> {
                        System.out.println("onErrorResume");
                        return Mono.empty();
                    })
            )
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

So essentially wrapping operations that might throw errors in flatMap or concatMap and using onErrorResume() on it. This way it produces the same result:

 input=1
input=2
onErrorResume
input=3
input=4
input=5
sum=26

6 Simulate onErrorContinue() with onErrorResume() and downstream onErrorContinue()

Sometimes onErrorContinue() is placed in the calling program and you have no control over it. But you still need onErrorResume(). what should you do?

 public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .flatMap(i -> Mono.just(i)
                    .map(j -> j == 2 ? j / 0 : j)
                    .map(j -> j * 2)
                    .onErrorResume(err -> {
                        System.out.println("onErrorResume");
                        return Mono.empty();
                    })
                    .onErrorStop()
            )
            .onErrorContinue((err, i) -> {log.info("onErrorContinue={}", i);})
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

The secret is to add onErrorStop() at the end of the onErrorResume() block - this blocks onErrorContinue() so it doesn't hold the error before onErrorResume(). Try removing onErrorStop() and you'll see onErrorContinue() pop up before onErrorResume.

Reactor onErrorContinue VS onErrorResume


程序员伍六七
201 声望597 粉丝