Cómo implementar código asíncrono anidada con la programación reactiva?

ProtossShuttle:

Soy muy nuevo en la programación reactiva. Aunque estoy muy familiarizado con la programación y Kotlin corrutinas funcionales, que todavía no puede encontrar la manera de utilizar los paradigmas de programación reactivos refactorizar código CRUD anidada normal, especialmente aquellos con operaciones asíncronas anidados.

Por ejemplo, a continuación es un simple código asíncrono CRUD fragmento basado en Java 8 CompletableFuture


        getFooAsync(id)
                .thenAccept(foo -> {
                    if (foo == null) {
                        insertFooAsync(id, new Foo());
                    } else {
                        getBarAsync(foo.bar)
                                .thenAccept(bar -> {
                                   updateBarAsync(foo, bar);
                                });
                    }
                });

Es muy fácil de refactorearlo con corrutinas Kotlin, lo que hace que sea mucho más fácil de leer sin perder la asincronía.

 val foo = suspendGetFoo(id)
 if(foo==null) {
   suspendInsertFoo(id, Foo())
 } else {
   val bar = suspendGetBar(foo.bar)
   suspendUpdateBar(foo, bar);-
}

Sin embargo, es un código de esa manera adecuada para la programación reactiva?

Si es así, dada una Flux<String> idFlux, cómo refactorizar con Reactor 3?

¿Es una buena idea simplemente reemplazar todos los CompletableFuturecon Mono?

Phil Arcilla:

es el código de esa manera adecuada para la programación reactiva?

corrutinas en mi humilde opinión, Kotlin son mucho más adecuados para este caso de uso, y dan lugar a un código más limpio mucho.

Sin embargo, usted puede hacer esto en las corrientes de reactivos.

¿Es una buena idea para simplemente reemplazar cada CompletableFuture con Mono?

He encontrado que las corrientes reactivas manejan una gran cantidad de casos de uso asincrónicos extremadamente bien (por ejemplo, los ejemplos de reactor de proyecto ). Sin embargo, definitivamente hay algunos casos de uso que no encajan mucho. Por lo tanto, no puedo recomendar una política de sustitución de cada CompletableFuture con las corrientes reactivas.

Sin embargo, un caso para el que debe cambiar lejos de CompletableFuturees cuando se necesita contrapresión.

Una gran cantidad de la decisión sobre qué modelo asíncrono debe usar depende de los idiomas / marcos / herramientas / bibliotecas que está utilizando y lo cómodo que usted y sus compañeros de equipo están con ellos. Si está utilizando las bibliotecas con un buen apoyo Kotlin, y su equipo está familiarizado con Kotlin, a continuación, utilizar corrutinas. Asimismo para los flujos reactivos.

dada una Flux<String> idFlux, cómo refactorizar con Reactor 3?

Aquí hay algunas cosas a tener en cuenta al considerar las corrientes reactivas para este caso de uso:

  1. Corrientes reactivas no pueden emitir null. En su lugar, un vacío Monose utiliza generalmente. (Se puede utilizar también técnicamente Mono<Optional<...>>, pero en ese momento sólo está dañando su cerebro y pidiendo insectos)
  2. Cuando un Monousuario está vacía, lambdas pasarse a cualquier operador que se ocupa de la onNextseñal (por ejemplo .map, .flatMap, .handle, etc.) son no invocan . Recuerde que usted está tratando con una corriente de datos (en lugar de un flujo de control imperativo)
  3. Las .switchIfEmptyo .defaultIfEmptylos operadores pueden operar en vacío Monos. Sin embargo, no proporcionan una elsecondición. Operadores aguas abajo no son conscientes de que la corriente estaba vacía previamente (a menos que el elemento que emite la Editorial pasó a .switchIfEmptyes fácilmente identificable de alguna manera)
  4. Si usted tiene una corriente de muchos operadores, y varios operadores podrían provocar que la corriente se vacíe, entonces es difícil / imposible que los operadores aguas abajo para determinar la razón por la corriente se convirtió en vacío.
  5. Los principales operadores asíncronos que permiten el procesamiento de los valores emitidos desde los operadores de aguas arriba son .flatMap, .flatMapSequentialy .concatMap. Usted tendrá que utilizar éstos para la cadena asíncrona operaciones que operan en la salida de las operaciones asincrónicas anteriores.
  6. Desde su caso de uso no regresar un valor, la puesta en práctica corriente reactiva devolverá unaMono<Void>

Una vez dicho todo esto, aquí es un intento de transformar su ejemplo para el reactor 3 (con algunas salvedades):

    Mono<Void> updateFoos(Flux<String> idFlux) {
        return idFlux                                         // Flux<String>
            .flatMap(id -> getFoo(id)                         // Mono<Foo>
                /*
                 * If a Foo with the given id is not found,
                 * create a new one, and continue the stream with it.
                 */
                .switchIfEmpty(insertFoo(id, new Foo()))      // Mono<Foo>
                /*
                 * Note that this is not an "else" condition
                 * to the above .switchIfEmpty
                 *
                 * The lambda passed to .flatMap will be
                 * executed with either:
                 * A) The foo found from getFoo
                 *    OR
                 * B) the newly inserted Foo from insertFoo
                 */
                .flatMap(foo -> getBar(foo.bar)               // Mono<Bar>
                    .flatMap(bar -> updateBar(foo, bar))      // Mono<Bar>
                    .then()                                   // Mono<Void>
                )                                             // Mono<Void>
            )                                                 // Flux<Void>
            .then();                                          // Mono<Void>
    }

    /*
     * @return the Foo with the given id, or empty if not found
     */
    abstract Mono<Foo> getFoo(String id);

    /*
     * @return the Bar with the given id, or empty if not found
     */
    abstract Mono<Bar> getBar(String id);

    /*
     * @return the Foo inserted, never empty
     */
    abstract Mono<Foo> insertFoo(String id, Foo foo);

    /*
     * @return the Bar updated, never empty
     */
    abstract Mono<Bar> updateBar(Foo foo, Bar bar);

y aquí está un ejemplo más complejo que utiliza una Tuple2<Foo,Boolean>para indicar si se encontró el original de Foo (esto debe ser semánticamente equivalente a su ejemplo):

    Mono<Void> updateFoos(Flux<String> idFlux) {
        return idFlux                                         // Flux<String>
            .flatMap(id -> getFoo(id)                         // Mono<Foo>
                /*
                 * Map to a Tuple2 whose t2 indicates whether the foo was found.
                 * In this case, it was found.
                 */
                .map(foo -> Tuples.of(foo, true))             // Mono<Tuple2<Foo,Boolean>>
                /*
                 * If a Foo with the given id is not found,
                 * create a new one, and continue the stream with 
                 * a Tuple2 indicating it wasn't originally found
                 */
                .switchIfEmpty(insertFoo(id, new Foo())       // Mono<Foo>
                    /*
                     * Foo was not originally found, so t2=false
                     */
                    .map(foo -> Tuples.of(foo, false)))       // Mono<Tuple2<Foo,Boolean>>
                /*
                 * The lambda passed to .flatMap will be
                 * executed with either:
                 * A) t1=foo found from getFoo, t2=true
                 *    OR
                 * B) t1=newly inserted Foo from insertFoo, t2=false
                 */
                .flatMap(tuple2 -> tuple2.getT2()
                    // foo originally found 
                    ? getBar(tuple2.getT1().bar)              // Mono<Bar>
                        .flatMap(bar -> updateBar(tuple2.getT1(), bar)) // Mono<Bar>
                        .then()                               // Mono<Void>
                    // foo originally not found (new inserted)
                    : Mono.empty()                            // Mono<Void>
                )
            )                                                 // Flux<Void>
            .then();                                          // Mono<Void>
    }

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=318138&siteId=1
Recomendado
Clasificación