Migliore strategia per riportare i progressi all'interfaccia utente: come dovrebbe avvenire la richiamata?


11

A volte l'utente avvia un'operazione tecnica estesa che richiede del tempo per essere eseguita. In questi casi, di solito è bello visualizzare una sorta di barra di avanzamento, insieme alle informazioni su quale attività è in esecuzione in questo momento.

Al fine di evitare un accoppiamento ravvicinato dell'interfaccia utente e dei livelli logici, in genere è preferibile che la comunicazione avvenga tramite un tipo di proxy. Cioè, il back-end non dovrebbe manipolare i propri elementi dell'interfaccia utente o interagire direttamente con il livello intermedio.

Ovviamente, ci deve essere qualche richiamo da qualche parte per farlo funzionare. In genere l'ho implementato in due modi:

  1. Passa un oggetto mutabile al back-end e fai in modo che il back-end lo modifichi in corso. L'oggetto notifica al front-end quando si verifica una modifica.

  2. Passare una funzione di richiamata del modulo void f(ProgressObject)o ProgressObject -> unitrichiamata dal back-end. In questo caso, il back-end costruisce ProgressObjected è completamente passivo. Deve costruire un nuovo oggetto ogni volta che vuole segnalare i progressi, suppongo.

Quali sono gli svantaggi e i vantaggi di questi metodi? Esiste un metodo migliore concordato da utilizzare? Ci sono diverse circostanze per il loro uso?

Esistono tecniche completamente diverse di segnalazione dei progressi che ho trascurato?


1
Per quanto riguarda mutevole vs immutabile, i vantaggi e gli svantaggi sono gli stessi di qualsiasi altro posto. Per quanto riguarda l'oggetto progress, questo può essere molto leggero; può essere semplice come un singolo numero: una percentuale.
Robert Harvey,

@RobertHarvey La dimensione dell'oggetto progress solitamente dipende dai requisiti dell'interfaccia utente. Guarda ad esempio la finestra di dialogo per la copia di Windows. Immagino che richieda molte informazioni.
GregRos,

1
@RobertHarvey Questa è una novità per me. Che cos'è?
GregRos,

1
Mi mordo. Usiamo BackgroundWorkerche RH menziona. Avvolto in una classe personalizzata insieme a un "modulo di avanzamento", ecc. E un semplice meccanismo per comunicare un'eccezione, come BackgroundWorkerda progettazione viene eseguito in un thread separato. Nella misura in cui usiamo le sue caratteristiche in un modo suggerito da .Net, allora si potrebbe dire che è idiomatico. E in qualsiasi contesto linguistico / quadro "idiomatico" può essere il migliore.
radarbob,

2
Non vedo differenze significative tra i tuoi due metodi. Un oggetto passato dal front-end al back-end che offre metodi che portano a una notifica del front-end ha effettivamente la funzione di un callback. E se il tuo secondo approccio usa un oggetto parametro più o meno complesso per passare informazioni, o se usa alcuni valori semplici non fa differenza dal punto di vista architettonico. In entrambi gli approcci il backend informa attivamente il frontend, le differenze sono solo dettagli minori, quindi non esiste un concetto diverso qui descritto.
Doc Brown,

Risposte:


8

Passa un oggetto mutabile al back-end e fai in modo che il back-end lo modifichi in corso. L'oggetto notifica al front-end quando si verifica una modifica.

È difficile bilanciare l'efficienza se il backend lo notifica a questo proposito. Senza cura potresti scoprire che l'incremento dei tuoi progressi finisce per raddoppiare o triplicare il tempo necessario per completare l'operazione se stai cercando un aggiornamento dei progressi molto regolare.

Passa una funzione di richiamata del modulo void f (ProgressObject) o ProgressObject -> unità richiamata dal back-end. In questo caso, il back-end costruisce ProgressObject ed è completamente passivo. Deve costruire un nuovo oggetto ogni volta che vuole segnalare i progressi, suppongo.

Non capisco molto le differenze qui.

Esistono tecniche completamente diverse di segnalazione dei progressi che ho trascurato?

Polling dal front-end in un thread separato con incrementi atomici nel back-end. Il polling ha senso in questo caso poiché si tratta di un'operazione che termina in un periodo finito e la probabilità che il frontend rilevi i cambiamenti di stato è alta, soprattutto se si sta mirando a una barra di avanzamento regolare e liscia. Potresti prendere in considerazione le variabili di condizione se non ti piace l'idea di eseguire il polling dal thread frontend, ma in tal caso potresti voler evitare di avvisare su ogni singolo incremento della barra di avanzamento granulare.


2

Questa è la differenza tra un meccanismo di notifica push and pull .

L'oggetto mutabile ( pull ) dovrà essere ripetutamente sottoposto a polling dall'interfaccia utente e sincronizzato se si prevede che l'attività back-end venga eseguita in un thread in background / worker.

Il callback ( push ) creerà lavoro per l'interfaccia utente solo quando qualcosa cambia effettivamente. Molti framework dell'interfaccia utente hanno anche un invokeOnUIThread richiamabile da un thread di lavoro per eseguire un pezzo di codice sul thread dell'interfaccia utente in modo da poter effettivamente apportare le modifiche senza dover affrontare i rischi correlati al thread. (gioco di parole)

In generale, le notifiche push sono preferibili perché funzionano solo quando il lavoro deve essere svolto.


1
Penso che quello che dici sia giusto in generale. Tuttavia, in questo caso particolare, una barra di avanzamento, possono verificarsi cambiamenti rapidamente. Se la tua aspettativa è che i "progressi" potrebbero cambiare molte volte al secondo, è più sensato utilizzare il modello pull, altrimenti dovrai preoccuparti che l'interfaccia utente riceva troppe notifiche da gestire.
Gort il robot il

L'invio di un oggetto progress può oscurare il meccanismo di notifica che stai utilizzando dal back-end, poiché forse l'oggetto progress sta eseguendo i call-back. In realtà non ho mai usato un meccanismo pull per quanto mi ricordo, e mi sono quasi dimenticato di questo: 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.- Non se l'oggetto mutabile è la finestra di dialogo stessa o un'interfaccia funzionante ad esso. Ovviamente, ciò equivale a una richiamata comunque.
Robert Harvey,

1
Eh? Il PO descrive chiaramente due diverse forme di meccanismo di spinta, in nessuna è necessario un polling.
Doc Brown,

0

Sto usando i websocket con AngularJS. Quando il front-end riceve un messaggio, lo visualizza in un'area del messaggio designata che scompare dopo alcuni secondi. Sul back-end ho appena pubblicato messaggi di stato in una coda di messaggi. Invio solo testo, ma non c'è motivo per cui non sia stato possibile inviare un oggetto stato con valori come percentuale di completamento o velocità di trasferimento.


0

Citi i tuoi "due modi" come se fossero concetti separati, ma io voglio insistere un po '.

  1. Passa un oggetto mutabile al back-end e fai in modo che il back-end lo modifichi in corso. L'oggetto notifica al front-end quando si verifica una modifica.

Hai già detto che vuoi evitare l'accoppiamento ravvicinato tra l'interfaccia utente e la logica, quindi posso tranquillamente supporre che questo "oggetto mutabile" che stai passando sia in realtà un'implementazione di una particolare interfaccia che è definita nel modulo logico. Come tale, questo è semplicemente un altro modo di trasferire una richiamata nel processo che periodicamente chiama con informazioni sui progressi.

Per quanto riguarda i vantaggi e gli svantaggi ...

Uno svantaggio per il metodo (1) è che la classe che implementa l'interfaccia può farlo solo una volta. (Se si desidera eseguire diversi lavori con invocazioni diverse, è necessario un'istruzione switch o il modello visitatore.) Con il metodo (2), lo stesso oggetto può utilizzare un callback diverso per ogni invocazione del codice backend senza la necessità di un interruttore.

Un punto di forza per il metodo (1) è che è molto più semplice avere più metodi sull'interfaccia piuttosto che gestire i callback multipli del metodo (2) o il singolo callback con un'istruzione switch per più contesti.


-2

Le tecniche che puoi usare possono essere molto diverse.

Provo a capire con uno scenario diverso

  • Richiesta di db
  • download file

Una semplice richiesta di accesso a db (media risposta da db con un elemt) non ha bisogno di un avanzamento del rapporto, ma alwasy può innescare il thread dell'interfaccia utente in un'attività separata es. asincrono o lavoratore in background, qui è sufficiente una richiamata per il risultato.

E se chiedessi di vedere tutto il tuo inventario con 1 mln di articoli? Il completamento di questa query dovrebbe richiedere alcuni minuti, quindi in questo caso è necessario implementare l'avanzamento di perport nella logica aziendale negli articoli / articoli del modulo, quindi è possibile aggiornare l'interfaccia utente e potrebbe essere disponibile l'opzione annulla richiamata.

Stesso caso per il download di file. Puoi sempre implementare qui il callback dei tuoi progressi in forma di byte di byte, e tenere tutto il controllo della comunicazione su http è molto comunemente modello.

Nel mio approccio personale, implemento la mia logica di avanzamento dell'attività solo ai miei clienti, evitando di condividere altri oggetti con l'end-point.


1
Questo in realtà non risponde alla domanda su vantaggi / svantaggi.
Benni,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.