Beste Strategie für die Meldung von Fortschritten an die Benutzeroberfläche - wie soll der Rückruf erfolgen?

11

Manchmal startet der Benutzer einen erweiterten technischen Vorgang, dessen Ausführung eine Weile dauert. In diesen Fällen ist es normalerweise hilfreich, eine Art Fortschrittsbalken zusammen mit Informationen darüber anzuzeigen, welche Aufgabe gerade ausgeführt wird.

Um eine enge Kopplung der Benutzeroberfläche und der Logikschichten zu vermeiden, ist es normalerweise am besten, die Kommunikation über eine Art Proxy durchzuführen. Das heißt, das Back-End sollte nicht seine eigenen UI-Elemente manipulieren oder sogar direkt mit der Zwischenschicht interagieren.

Offensichtlich muss es irgendwo einen Rückruf geben, damit dies funktioniert. Ich habe es im Allgemeinen auf zwei Arten implementiert:

  1. Übergeben Sie ein veränderbares Objekt an das Back-End, und lassen Sie das Back-End im Verlauf Änderungen daran vornehmen. Das Objekt benachrichtigt das Front-End, wenn eine Änderung auftritt.

  2. Übergeben Sie eine Rückruffunktion des Formulars void f(ProgressObject)oder ProgressObject -> unitdes vom Backend aufgerufenen Formulars . In diesem Fall erstellt das Back-End das ProgressObjectund es ist vollständig passiv. Ich nehme an, es muss jedes Mal ein neues Objekt erstellen, wenn es den Fortschritt melden möchte.

Was sind die Nachteile und Vorteile dieser Methoden? Gibt es eine vereinbarte beste Methode? Gibt es unterschiedliche Umstände für ihre Verwendung?

Gibt es völlig andere Techniken zur Berichterstattung über Fortschritte, die ich übersehen habe?

GregRos
quelle
1
In Bezug auf veränderlich und unveränderlich sind die Vor- und Nachteile dieselben wie anderswo. In Bezug auf das Fortschrittsobjekt kann dies sehr leicht sein; Es kann so einfach wie eine einzelne Zahl sein: ein Prozentsatz.
Robert Harvey
@RobertHarvey Die Größe des Fortschrittsobjekts hängt normalerweise von den Anforderungen der Benutzeroberfläche ab. Schauen Sie sich zum Beispiel den Windows-Kopierdialog an. Ich stelle mir vor, es erfordert viele Informationen.
GregRos
1
@ RobertHarvey Das sind Neuigkeiten für mich. Was ist es?
GregRos
1
Ich werde beißen. Wir verwenden BackgroundWorkerdiese RH-Erwähnungen. Eingewickelt in eine benutzerdefinierte Klasse zusammen mit einem "Fortschrittsformular" usw. und einem einfachen Mechanismus zum Kommunizieren einer Ausnahme - wie BackgroundWorkerbeabsichtigt in einem separaten Thread ausgeführt. In dem Maße, in dem wir seine Funktionen auf eine von .Net vorgeschlagene Weise verwenden, könnte man sagen, dass dies idiomatisch ist. Und in jedem gegebenen Sprach- / Rahmenkontext kann "idiomatisch" am besten sein.
Radarbob
2
Ich sehe keine signifikanten Unterschiede zwischen Ihren beiden Methoden. Ein Objekt, das vom Frontend an das Backend übergeben wird und Methoden bietet, die zu einer Benachrichtigung des Frontends führen, hat tatsächlich die Funktion eines Rückrufs. Und wenn Ihr zweiter Ansatz ein mehr oder weniger komplexes Parameterobjekt zum Übergeben von Informationen verwendet oder wenn einige einfache Werte verwendet werden, macht dies aus architektonischer Sicht keinen Unterschied. In beiden Ansätzen informiert das Backend das Frontend aktiv, die Unterschiede sind nur geringfügige Details, so dass hier kein anderes Konzept beschrieben wird.
Doc Brown

Antworten:

8

Übergeben Sie ein veränderbares Objekt an das Back-End, und lassen Sie das Back-End im Verlauf Änderungen daran vornehmen. Das Objekt benachrichtigt das Front-End, wenn eine Änderung auftritt.

Es ist schwierig, die Effizienz auszugleichen, wenn das Backend diesbezüglich benachrichtigt. Ohne Sorgfalt können Sie feststellen, dass das Erhöhen Ihres Fortschritts die Zeit, die zum Abschließen des Vorgangs benötigt wird, verdoppelt oder verdreifacht, wenn Sie eine sehr reibungslose Fortschrittsaktualisierung anstreben.

Übergeben Sie eine Rückruffunktion der Form void f (ProgressObject) oder ProgressObject -> Unit, die das Backend aufruft. In diesem Fall erstellt das Back-End das ProgressObject und es ist vollständig passiv. Ich nehme an, es muss jedes Mal ein neues Objekt erstellen, wenn es den Fortschritt melden möchte.

Ich verstehe den Unterschied hier nicht so sehr.

Gibt es völlig andere Techniken zur Berichterstattung über Fortschritte, die ich übersehen habe?

Abfrage vom Frontend in einem separaten Thread mit atomaren Inkrementen im Backend. Abfragen sind hier sinnvoll, da es sich um eine Operation handelt, die in einem begrenzten Zeitraum abgeschlossen wird und die Wahrscheinlichkeit, dass das Frontend Statusänderungen erfasst, hoch ist, insbesondere wenn Sie einen seidenweichen Fortschrittsbalken anstreben. Sie können Bedingungsvariablen in Betracht ziehen, wenn Sie die Idee des Abrufs vom Frontend-Thread nicht mögen. In diesem Fall möchten Sie jedoch möglicherweise vermeiden, bei jedem einzelnen granularen Fortschrittsbalkeninkrement eine Benachrichtigung zu erhalten.


quelle
2

Dies ist der Unterschied zwischen einem Push- und Pull- Benachrichtigungsmechanismus.

Das veränderbare Objekt (das Pull ) muss von der Benutzeroberfläche wiederholt abgefragt und synchronisiert werden, wenn Sie erwarten, dass die Back-End-Aufgabe in einem Hintergrund- / Worker-Thread ausgeführt wird.

Der Rückruf (der Push ) schafft nur dann Arbeit für die Benutzeroberfläche, wenn sich tatsächlich etwas ändert. Viele UI-Frameworks verfügen auch über einen invokeOnUIThread, der von einem Worker-Thread aufgerufen werden kann, damit ein Teil des Codes auf dem UI-Thread ausgeführt wird, sodass Sie die Änderungen tatsächlich vornehmen können, ohne auf threadbezogene Gefahren einzugehen. (Wortspiel beabsichtigt)

Im Allgemeinen sind Push- Benachrichtigungen vorzuziehen, da sie nur dann funktionieren, wenn Arbeit erledigt werden muss.

Ratschenfreak
quelle
1
Ich denke, was Sie sagen, ist im Allgemeinen richtig. In diesem speziellen Fall, einem Fortschrittsbalken, können Änderungen jedoch schnell auftreten. Wenn Sie davon ausgehen, dass sich der "Fortschritt" wahrscheinlich mehrmals pro Sekunde ändert, ist es sinnvoller, das Pull-Modell zu verwenden, da Sie sich sonst Sorgen machen müssen, dass die Benutzeroberfläche zu viele Benachrichtigungen erhält, um verarbeitet zu werden.
Gort the Robot
Das Senden eines Fortschrittsobjekts kann den von Ihnen verwendeten Benachrichtigungsmechanismus im Back-End verdecken, da das Fortschrittsobjekt möglicherweise die Rückrufe ausführt. Soweit ich mich erinnere, habe ich noch nie einen
Zugmechanismus verwendet
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.- Nicht, wenn das veränderbare Objekt der Dialog selbst oder eine Arbeitsschnittstelle dazu ist. Das ist natürlich sowieso ein Rückruf.
Robert Harvey
1
Huh? Das OP beschreibt klar zwei verschiedene Formen eines Push-Mechanismus, bei denen keine Abfrage erforderlich ist.
Doc Brown
0

Ich verwende Websockets mit AngularJS. Wenn das Front-End eine Nachricht empfängt, wird sie in einem bestimmten Nachrichtenbereich angezeigt, der nach einigen Sekunden leer wird. Am Backend poste ich einfach Statusnachrichten in einer Nachrichtenwarteschlange. Ich sende nur Text, aber es gibt keinen Grund, warum ich kein Statusobjekt mit Werten wie Prozentsatz abgeschlossen oder Übertragungsgeschwindigkeit senden konnte.

TMN
quelle
0

Sie erwähnen Ihre "zwei Wege", als wären sie getrennte Konzepte, aber ich möchte darauf ein wenig zurückkommen.

  1. Übergeben Sie ein veränderbares Objekt an das Back-End, und lassen Sie das Back-End im Verlauf Änderungen daran vornehmen. Das Objekt benachrichtigt das Front-End, wenn eine Änderung auftritt.

Sie haben bereits gesagt, dass Sie eine enge Kopplung der Benutzeroberfläche und der Logik vermeiden möchten, sodass ich davon ausgehen kann, dass dieses "veränderbare Objekt", das Sie übergeben, tatsächlich eine Implementierung einer bestimmten Schnittstelle ist, die im Logikmodul definiert ist. Als solches ist dies lediglich eine andere Möglichkeit, einen Rückruf an den Prozess weiterzuleiten, der regelmäßig mit Informationen über den Fortschritt aufgerufen wird.

Vor- und Nachteile ...

Ein Nachteil für Methode (1) ist, dass die Klasse, die die Schnittstelle implementiert, dies nur einmal tun kann. (Wenn Sie verschiedene Jobs mit unterschiedlichen Aufrufen ausführen möchten, benötigen Sie eine switch-Anweisung oder das Besuchermuster.) Mit Methode (2) kann dasselbe Objekt für jeden Aufruf des Backend-Codes einen anderen Rückruf verwenden, ohne dass ein erforderlich ist Schalter.

Eine Stärke von Methode (1) besteht darin, dass es viel einfacher ist, mehrere Methoden auf der Schnittstelle zu haben, als die mehrfachen Rückrufe von Methode (2) oder einen einzelnen Rückruf mit einer switch-Anweisung für mehrere Kontexte zu behandeln.

Daniel T.
quelle
-2

Die Techniken, die Sie verwenden können, können sehr unterschiedlich sein.

Ich versuche es mit einem anderen Szenario herauszufinden

  • Anfrage an db
  • Download-Datei

Eine einfache Anmeldeanforderung an db (mittlere Antwort von db mit einem Element) benötigt keinen Berichtsfortschritt, kann jedoch immer den UI-Thread in einer separaten Task abfeuern. Async oder Backgroundworker, hier benötigen Sie nur einen Rückruf für das Ergebnis.

Aber was ist, wenn Sie nach Ihrem gesamten Inventar mit 1 Mio. Artikel fragen? Diese Abfrage sollte einige Minuten dauern. In diesem Fall müssen Sie Ihren Perport-Fortschritt in Ihrer Geschäftslogik in Formularelementen implementieren. Anschließend können Sie Ihre Benutzeroberfläche aktualisieren und erhalten möglicherweise die Option Rückruf abbrechen.

Gleicher Fall für den Dateidownload. Sie können Ihren Fortschrittsrückruf hier jederzeit in Form von Bytes implementieren und die gesamte Kommunikationskontrolle über http als Muster beibehalten.

Bei meinem persönlichen Ansatz implementiere ich meine Geschäftsfortschrittslogik nur für meine Kunden, ohne andere Objekte mit dem Endpunkt zu teilen.

Marco Luongo
quelle
1
Dies beantwortet die Frage nach Vor- und Nachteilen nicht wirklich.
Benni