La mejor estrategia para informar el progreso a la interfaz de usuario: ¿cómo debe ocurrir la devolución de llamada?


11

A veces, el usuario inicia una operación técnica extendida que demora un tiempo en ejecutarse. En estos casos, generalmente es bueno mostrar algún tipo de barra de progreso, junto con información sobre qué tarea se está ejecutando en este momento.

Para evitar el acoplamiento cercano de la interfaz de usuario y las capas lógicas, generalmente es mejor que la comunicación se produzca a través de algún tipo de proxy. Es decir, el back-end no debe manipular sus propios elementos de la interfaz de usuario, o incluso interactuar directamente con la capa intermedia.

Obviamente, debe haber alguna devolución de llamada en algún lugar para que esto funcione. Generalmente lo he implementado de una de dos maneras:

  1. Pase un objeto mutable al back-end y haga que el back-end le haga cambios en el progreso. El objeto notifica al front-end cuando ocurre un cambio.

  2. Pase una función de devolución de llamada del formulario void f(ProgressObject)o ProgressObject -> unitque invoque el back-end. En este caso, el back-end construye ProgressObjecty es completamente pasivo. Supongo que debe construir un nuevo objeto cada vez que quiera informar sobre el progreso.

¿Cuáles son los inconvenientes y las ventajas de estos métodos? ¿Existe un mejor método acordado para usar? ¿Existen diferentes circunstancias para su uso?

¿Hay técnicas completamente diferentes de informar el progreso que he pasado por alto?


1
En cuanto a mutable vs inmutable, las ventajas y los inconvenientes son los mismos que en cualquier otro lugar. En cuanto al objeto de progreso, esto puede ser muy ligero; puede ser tan simple como un solo número: un porcentaje.
Robert Harvey

@RobertHarvey El tamaño del objeto de progreso generalmente depende de los requisitos de la interfaz de usuario. Mire el diálogo de copia de Windows, por ejemplo. Me imagino que requiere mucha información.
GregRos

1
@RobertHarvey Eso es nuevo para mí. ¿Qué es?
GregRos

1
Morderé. Usamos las BackgroundWorkermenciones de RH. Envuelto en una clase personalizada junto con un "formulario de progreso", etc. y un mecanismo simple para comunicar una excepción, como BackgroundWorkerpor diseño se ejecuta en un hilo separado. En la medida en que usemos sus características de una manera sugerida por .Net, entonces podría decirse que es idiomático. Y en cualquier contexto de lenguaje / marco dado, "idiomático" puede ser lo mejor.
radarbob

2
No veo diferencias significativas entre sus dos métodos. Un objeto pasado del front-end al back-end que ofrece métodos que conducen a una notificación del front-end tiene en realidad la función de una devolución de llamada. Y si su segundo enfoque usa un objeto de parámetro más o menos complejo para pasar información, o si usa algunos valores simples, no hace una diferencia desde un punto de vista arquitectónico. En ambos enfoques, el backend informa activamente a la interfaz, las diferencias son solo detalles menores, por lo que no se describe aquí un concepto diferente.
Doc Brown

Respuestas:


8

Pase un objeto mutable al back-end y haga que el back-end le haga cambios en el progreso. El objeto notifica al front-end cuando ocurre un cambio.

Es difícil equilibrar la eficiencia si el backend lo notifica a este respecto. Sin cuidado, es posible que aumentar su progreso termine duplicando o triplicando el tiempo que lleva completar la operación si está buscando una actualización de progreso muy fluida.

Pase una función de devolución de llamada del formulario void f (ProgressObject) o ProgressObject -> unidad que invoca el back-end. En este caso, el back-end construye el ProgressObject y es completamente pasivo. Supongo que debe construir un nuevo objeto cada vez que quiera informar sobre el progreso.

No entiendo mucho la diferencia aquí.

¿Hay técnicas completamente diferentes de informar el progreso que he pasado por alto?

Encuesta desde el front-end en un hilo separado con incrementos atómicos en el backend. El sondeo tiene sentido aquí, ya que es para una operación que finaliza en un período finito y la probabilidad de que el frontend recoja los cambios de estado es alta, especialmente si está buscando una barra de progreso suave y sedosa. Puede considerar las variables de condición si no le gusta la idea de sondeo desde el hilo de la interfaz, pero en ese caso es posible que desee evitar notificar cada incremento granular de la barra de progreso.


2

Esta es la diferencia entre un mecanismo de notificación push y pull .

El objeto mutable (la extracción ) deberá ser sondeado repetidamente por la interfaz de usuario y sincronizado si espera que la tarea de fondo se ejecute en un subproceso de fondo / trabajador.

La devolución de llamada (la inserción ) solo creará trabajo para la interfaz de usuario cuando algo realmente cambie. Muchos marcos de UI también tienen un invokeOnUIThread invocable desde un subproceso de trabajo para hacer que se ejecute un fragmento de código en el subproceso de la IU para que pueda realizar los cambios sin entrar en peligro relacionado con el subproceso. (juego de palabras previsto)

En general, las notificaciones push son preferibles porque solo hacen el trabajo cuando hay que hacerlo.


1
Creo que lo que dices es correcto en general. Sin embargo, para este caso particular, una barra de progreso, los cambios pueden ocurrir rápidamente. Si su expectativa es que es probable que el "progreso" cambie muchas veces por segundo, tiene más sentido usar el modelo de extracción, ya que de lo contrario tendrá que preocuparse de que la IU reciba demasiadas notificaciones para manejar.
Gort the Robot

Enviar un objeto de progreso puede oscurecer el mecanismo de notificación que está utilizando desde el back-end, ya que tal vez el objeto de progreso está haciendo las devoluciones de llamada. En realidad, nunca he usado un mecanismo de extracción por lo que recuerdo, y de alguna manera lo olvidé: P
GregRos

The mutable object (the pull) will need to be repeatably polled by the UI and synchronized if you expect the back-end task to be executed in a background/worker thread.- No si el objeto mutable es el diálogo en sí, o una interfaz de trabajo para él. Por supuesto, eso equivale a una devolución de llamada de todos modos.
Robert Harvey

1
¿Eh? El OP describe claramente dos formas diferentes de un mecanismo de empuje, en ninguna es necesaria una encuesta.
Doc Brown

0

Estoy usando websockets con AngularJS. Cuando el front end recibe un mensaje, lo muestra en un área de mensaje designada que se desvanece en blanco después de unos segundos. En el back-end solo publico mensajes de estado en una cola de mensajes. Solo envío mensajes de texto, pero no hay razón para no poder enviar un objeto de estado con valores como porcentaje completado o velocidad de transferencia.


0

Mencionas tus "dos formas" como si fueran conceptos separados pero quiero retrasar un poco eso.

  1. Pase un objeto mutable al back-end y haga que el back-end le haga cambios en el progreso. El objeto notifica al front-end cuando ocurre un cambio.

Ya ha dicho que desea evitar el acoplamiento estrecho de la interfaz de usuario y la lógica, por lo que puedo suponer con seguridad que este "objeto mutable" que está pasando es en realidad una implementación de una interfaz particular que se define en el módulo lógico. Como tal, esta es simplemente otra forma de pasar una devolución de llamada al proceso que periódicamente llama con información sobre el progreso.

En cuanto a los beneficios y desventajas ...

Un inconveniente del método (1) es que la clase que implementa la interfaz solo puede hacerlo una vez. (Si desea realizar diferentes trabajos con diferentes invocaciones, necesitará una declaración de cambio o el patrón de visitante). Con el método (2), el mismo objeto puede usar una devolución de llamada diferente para cada invocación del código de fondo sin la necesidad de un cambiar.

Una fortaleza del método (1) es que es mucho más fácil tener múltiples métodos en la interfaz que lidiar con las múltiples devoluciones de llamada o la devolución de llamada única del método (2) con una declaración de conmutación para múltiples contextos.


-2

Las técnicas que puede usar pueden ser muy diferentes.

Trato de averiguar con diferentes escenarios

  • Solicitud a db
  • descargar archivo

Una simple solicitud de inicio de sesión a db (respuesta media de db con un elemento) no necesita un informe de progreso, pero siempre puede disparar el hilo de la interfaz de usuario en tareas separadas, por ejemplo. Async o backgroundworker, aquí solo necesita una devolución de llamada para el resultado.

Pero, ¿qué sucede si consulta para ver todo su inventario con un artículo de 1 mln? Esta consulta debería tardar varios minutos en completarse, por lo que en este caso debe implementar su progreso de perport en su lógica de negocios en elementos / elementos de formulario, luego puede actualizar su UI y se le puede dar la opción de cancelar la devolución de llamada.

El mismo caso para la descarga de archivos. Siempre puede implementar aquí su devolución de llamada de progreso en bytes de forma, y ​​mantener todo el control de comunicación sobre http es un patrón muy común.

En mi enfoque personal, implemento la lógica de progreso de mi negocio solo para mis clientes, evitando compartir otro objeto con el punto final.


1
Esto realmente no responde la pregunta sobre ventajas / desventajas.
Benni
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.