java - callbacks - nodejs wait for callback
¿Patrón de Java para devoluciones de llamada anidadas? (4)
Estoy buscando un patrón de Java para hacer una secuencia anidada de llamadas a métodos sin bloqueo. En mi caso, algún código de cliente necesita invocar asíncronamente un servicio para realizar algún caso de uso, y cada paso de ese caso de uso debe realizarse de forma asíncrona (por razones que están fuera del alcance de esta pregunta). Imagine que tengo interfaces existentes de la siguiente manera:
public interface Request {}
public interface Response {}
public interface Callback<R extends Response> {
void onSuccess(R response);
void onError(Exception e);
}
Existen varias implementaciones emparejadas de las interfaces de Request
y Response
, concretamente RequestA
+ ResponseA
(otorgada por el cliente), RequestB
+ ResponseB
(utilizada internamente por el servicio), etc.
El flujo de procesamiento se ve así:
Entre el recibo de cada respuesta y el envío de la siguiente solicitud, debe realizarse algún procesamiento adicional (por ejemplo, en función de los valores de cualquiera de las solicitudes o respuestas anteriores).
Hasta ahora he intentado dos enfoques para codificar esto en Java:
- Clases anónimas: se pone feo rápidamente debido a la anidación requerida
- clases internas: más ordenadas que las anteriores, pero aún es difícil para otro desarrollador comprender el flujo de ejecución
¿Hay algún patrón para hacer que este código sea más legible? Por ejemplo, ¿podría expresar el método de servicio como una lista de operaciones autónomas que se ejecutan en secuencia por alguna clase de marco que se encarga de la anidación?
Como la implementación (no solo la interfaz) no debe bloquearse, me gusta su idea de lista.
Configure una lista de "operaciones" (¿quizás Future
s?), Para las cuales la configuración debe ser bastante clara y legible. Luego al recibir cada respuesta, se debe invocar la siguiente operación.
Con un poco de imaginación, esto suena como la cadena de responsabilidad . Aquí hay un pseudocódigo para lo que estoy imaginando:
public void setup() {
this.operations.add(new Operation(new RequestA(), new CallbackA()));
this.operations.add(new Operation(new RequestB(), new CallbackB()));
this.operations.add(new Operation(new RequestC(), new CallbackC()));
this.operations.add(new Operation(new RequestD(), new CallbackD()));
startNextOperation();
}
private void startNextOperation() {
if ( this.operations.isEmpty() ) { reportAllOperationsComplete(); }
Operation op = this.operations.remove(0);
op.request.go( op.callback );
}
private class CallbackA implements Callback<Boolean> {
public void onSuccess(Boolean response) {
// store response? etc?
startNextOperation();
}
}
...
En mi opinión, la forma más natural de modelar este tipo de problema es con Future<V>
.
Por lo tanto, en lugar de utilizar una devolución de llamada, simplemente devuelva un "thunk": un Future<Response>
que representa la respuesta que estará disponible en algún momento en el futuro.
Luego, puede modelar pasos posteriores como cosas como Future<ResponseB> step2(Future<ResponseA>)
, o usar ListenableFuture<V>
desde Guava. Luego puede usar Futures.transform()
o una de sus sobrecargas para encadenar sus funciones de forma natural, pero conservando la naturaleza asincrónica.
Si se usa de esta manera, Future<V>
comporta como una mónada (de hecho, creo que puede calificar como uno, aunque no estoy seguro de la cabeza), por lo que todo el proceso se parece un poco a IO en Haskell como se realiza a través de la mónada IO.
No estoy seguro si recibo tu pregunta correctamente. Si desea invocar un servicio y en su resultado de finalización, debe pasarlo a otro objeto que puede seguir procesando utilizando el resultado. Puedes ver usando Composite and Observer para lograr esto.
Puede usar el modelo informático actor. En su caso, el cliente, los servicios y las devoluciones de llamada [BD] pueden ser representados como actores.
Hay muchas bibliotecas de actores para Java. La mayoría de ellos, sin embargo, son pesados, así que escribí uno compacto y extensible: df4j . Considera el modelo de actor como un caso específico del modelo informático de flujo de datos más general y, como resultado, permite al usuario crear nuevos tipos de actores, para ajustarse de manera óptima a los requisitos del usuario.