Combinando editores no Project Reactor

Combinando editores no Project Reactor

*1. Visão geral *

Neste artigo, veremos várias maneiras de combinar Publishers em Project Reactor.

===* 2. Dependências do Maven *

Vamos configurar nosso exemplo com o Reator de projeto dependências:

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <version>3.1.4.RELEASE</version>
    <scope>test</scope>
</dependency>

===* 3. Combinando editores *

Dado um cenário em que é necessário trabalhar com _Flux <T> _ ou _Mono <T> _, existem diferentes maneiras de combinar fluxos.

Vamos criar alguns exemplos para ilustrar o uso de métodos estáticos na classe Flux <T> _, como _concat, concatWith, merge, zip e combineLatest.

Nossos exemplos farão uso de dois editores do tipo Flux <Integer> _, ou seja, _evenNumbers, que é um Flux de Integer e mantém uma sequência de números pares começando com 1 (variável min) e limitada por 5 (variável max).

Criaremos oddNumbers, também um Flux do tipo Integer de números ímpares:

Flux<Integer> evenNumbers = Flux
  .range(min, max)
  .filter(x -> x % 2 == 0);//i.e. 2, 4

Flux<Integer> oddNumbers = Flux
  .range(min, max)
  .filter(x -> x % 2 > 0); //ie. 1, 3, 5

====* 3.1 concat () *

O método concat executa uma concatenação das entradas, encaminhando elementos emitidos pelas fontes a jusante.

A concatenação é obtida assinando sequencialmente a primeira fonte e aguardando a conclusão antes de assinar a próxima, e assim sucessivamente até a última fonte ser concluída. Qualquer erro interrompe a sequência imediatamente e é encaminhado a jusante.

Aqui está um exemplo rápido:

@Test
public void givenFluxes_whenConcatIsInvoked_thenConcat() {
    Flux<Integer> fluxOfIntegers = Flux.concat(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

====* 3.2. * _ConcatWith () _

Usando o método estático concatWith, produziremos uma concatenação de duas fontes do tipo _Flux <T> _ como resultado:

@Test
public void givenFluxes_whenConcatWithIsInvoked_thenConcatWith() {
    Flux<Integer> fluxOfIntegers = evenNumbers.concatWith(oddNumbers);

   //same stepVerifier as in the concat example above
}

3.3 combineLatest ()

O método estático do Flux combineLatest gerará dados fornecidos pela combinação do valor publicado mais recentemente de cada uma das fontes do Publicador.

Aqui está um exemplo do uso desse método com duas fontes Publisher e uma BiFunction como parâmetros:

@Test
public void givenFluxes_whenCombineLatestIsInvoked_thenCombineLatest() {
    Flux<Integer> fluxOfIntegers = Flux.combineLatest(
      evenNumbers,
      oddNumbers,
      (a, b) -> a + b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(5)//4 + 1
      .expectNext(7)//4 + 3
      .expectNext(9)//4 + 5
      .expectComplete()
      .verify();
}

Podemos ver aqui que a função combineLatest aplicou a função “a + b” usando o elemento mais recente de evenNumbers (4) e os elementos de oddNumbers (1,3,5) _, gerando a sequência _5,7,9.

3.4 _merge () _

A função merge executa uma mesclagem dos dados das sequências Publisher contidas em uma matriz em uma sequência mesclada intercalada:

@Test
public void givenFluxes_whenMergeIsInvoked_thenMerge() {
    Flux<Integer> fluxOfIntegers = Flux.merge(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

Uma coisa interessante a ser observada é que, ao contrário de concat (lazy subscription ) , as fontes são inscritas ansiosamente.

Aqui, podemos ver um resultado diferente da função merge se inserirmos um atraso entre os elementos dos Publicadores:

@Test
public void givenFluxes_whenMergeWithDelayedElementsIsInvoked_thenMergeWithDelayedElements() {
    Flux<Integer> fluxOfIntegers = Flux.merge(
      evenNumbers.delayElements(Duration.ofMillis(500L)),
      oddNumbers.delayElements(Duration.ofMillis(300L)));

    StepVerifier.create(fluxOfIntegers)
      .expectNext(1)
      .expectNext(2)
      .expectNext(3)
      .expectNext(5)
      .expectNext(4)
      .expectComplete()
      .verify();
}

3.5. _mergeSequential () _

O método mergeSequential mescla dados das sequências Publisher fornecidas em uma matriz em uma sequência mesclada ordenada.

Ao contrário de concat, as fontes são inscritas ansiosamente.

Além disso, diferentemente de merge, seus valores emitidos são mesclados na sequência final na ordem de assinatura:

@Test
public void testMergeSequential() {
    Flux<Integer> fluxOfIntegers = Flux.mergeSequential(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

3.6. _mergeDelayError () _

O mergeDelayError mescla dados das sequências do Publisher contidas em uma matriz em uma sequência mesclada intercalada.

Ao contrário de concat, as fontes são inscritas ansiosamente.

Essa variante do método estático merge atrasará qualquer erro até que o restante do backlog de mesclagem tenha sido processado.

Aqui está um exemplo de _mergeDelayError: _

@Test
public void givenFluxes_whenMergeWithDelayedElementsIsInvoked_thenMergeWithDelayedElements() {
    Flux<Integer> fluxOfIntegers = Flux.mergeDelayError(1,
      evenNumbers.delayElements(Duration.ofMillis(500L)),
      oddNumbers.delayElements(Duration.ofMillis(300L)));

    StepVerifier.create(fluxOfIntegers)
      .expectNext(1)
      .expectNext(2)
      .expectNext(3)
      .expectNext(5)
      .expectNext(4)
      .expectComplete()
      .verify();
}

* 3,7. fundir com() *

O método estático mergeWith mescla dados deste Flux e um Publisher em uma sequência mesclada intercalada.

Novamente, ao contrário de concat, as fontes internas são inscritas ansiosamente:

@Test
public void givenFluxes_whenMergeWithIsInvoked_thenMergeWith() {
    Flux<Integer> fluxOfIntegers = evenNumbers.mergeWith(oddNumbers);

   //same StepVerifier as in "3.4. Merge"
    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

====* 3.8 fecho eclair()__ *

O método estático zip aglutina várias fontes juntas, ou seja, espera que todas as fontes emitam um elemento e combina esses elementos em um valor de saída (construído pela função combinadora fornecida).

O operador continuará fazendo isso até que qualquer uma das fontes seja concluída:

@Test
public void givenFluxes_whenZipIsInvoked_thenZip() {
    Flux<Integer> fluxOfIntegers = Flux.zip(
      evenNumbers,
      oddNumbers,
      (a, b) -> a + b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(3)//2 + 1
      .expectNext(7)//4 + 3
      .expectComplete()
      .verify();
}

Como não resta nenhum elemento de evenNumbers para emparelhar, o elemento 5 do oddNumbers Publisher é ignorado.

====* 3.9 zipWith () *

O zipWith executa o mesmo método que zip, mas apenas com dois editores:

@Test
public void givenFluxes_whenZipWithIsInvoked_thenZipWith() {
    Flux<Integer> fluxOfIntegers = evenNumbers
     .zipWith(oddNumbers, (a, b) -> a* b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2) //2 *1
      .expectNext(12)//4* 3
      .expectComplete()
      .verify();
}

4. Conclusão

Neste tutorial rápido, descobrimos várias maneiras de combinar Publishers.

Como sempre, os exemplos estão disponíveis em over no GitHub.