Najlepsza strategia raportowania postępu do interfejsu użytkownika - jak powinno nastąpić oddzwanianie?

11

Czasami użytkownik rozpoczyna rozszerzoną operację techniczną, której wykonanie zajmuje trochę czasu. W takich przypadkach zazwyczaj miło jest wyświetlić pasek postępu wraz z informacją o tym, które zadanie jest aktualnie wykonywane.

Aby uniknąć ścisłego powiązania interfejsu użytkownika i warstw logicznych, zwykle najlepiej jest, aby komunikacja odbywała się za pośrednictwem pewnego rodzaju serwera proxy. Oznacza to, że zaplecze nie powinno manipulować własnymi elementami interfejsu użytkownika, ani nawet bezpośrednio oddziaływać z warstwą pośrednią.

Oczywiście musi być jakieś oddzwonienie, aby to zadziałało. Generalnie zaimplementowałem go na dwa sposoby:

  1. Przekaż zmienny obiekt do zaplecza i niech zaplecze wprowadzi do niego zmiany w miarę postępu. Obiekt powiadamia interfejs użytkownika, gdy nastąpi zmiana.

  2. Przekaż funkcję zwrotną formularza void f(ProgressObject)lub ProgressObject -> unitwywołaną przez back-end. W tym przypadku back-end konstruuje ProgressObjecti jest całkowicie pasywny. Zakładam, że musi on skonstruować nowy obiekt za każdym razem, gdy chce zgłosić postęp.

Jakie są wady i zalety tych metod? Czy istnieje uzgodniona najlepsza metoda użycia? Czy istnieją różne okoliczności ich użycia?

Czy przeoczyłem zupełnie inne techniki raportowania postępów?

GregRos
źródło
1
Jeśli chodzi o zmienną vs niezmienną, zalety i wady są takie same, jak gdziekolwiek indziej. W odniesieniu do obiektu postępu może to być bardzo lekkie; może być tak prosta jak pojedyncza liczba: procent.
Robert Harvey,
@RobertHarvey Rozmiar obiektu postępu zwykle zależy od wymagań interfejsu użytkownika. Spójrz na przykład na okno dialogowe kopiowania Windows. Wyobrażam sobie, że wymaga dużo informacji.
GregRos,
1
@RobertHarvey To dla mnie wiadomość. Co to jest?
GregRos,
1
Ugryzę. Używamy BackgroundWorkertego, o którym wspomina RH. Zawinięte w niestandardową klasę wraz z „formularzem postępu” itp. Oraz prostym mechanizmem komunikowania wyjątku - jak BackgroundWorkerz założenia działa w osobnym wątku. W zakresie, w jakim korzystamy z jego funkcji w sposób sugerowany przez .Net, można to uznać za idiomatyczne. W każdym kontekście języka / frameworku „idiomatyczny” może być najlepszy.
radarbob,
2
Nie widzę żadnych istotnych różnic między waszymi dwiema metodami. Obiekt przekazywany z interfejsu do zaplecza, który oferuje metody prowadzące do powiadomienia o interfejsie, ma w rzeczywistości funkcję wywołania zwrotnego. A jeśli twoje drugie podejście wykorzystuje mniej lub bardziej złożony obiekt parametru do przekazywania informacji lub jeśli używa kilku prostych wartości, nie robi to różnicy z architektonicznego punktu widzenia. W obu podejściach backend aktywnie informuje frontend, różnice są tylko drobnymi szczegółami, więc nie opisano tutaj innej koncepcji.
Doc Brown,

Odpowiedzi:

8

Przekaż zmienny obiekt do zaplecza i niech zaplecze wprowadzi do niego zmiany w miarę postępu. Obiekt powiadamia interfejs użytkownika, gdy nastąpi zmiana.

Trudno zrównoważyć wydajność, jeśli backend powiadamia o tym. Beztroskie może okazać się, że zwiększenie twojego postępu może podwoić lub potroić czas potrzebny do ukończenia operacji, jeśli dążysz do bardzo płynnej aktualizacji postępu.

Przekaż funkcję zwrotną w postaci void f (ProgressObject) lub ProgressObject -> jednostka wywoływana przez back-end. W tym przypadku back-end konstruuje ProgressObject i jest całkowicie pasywny. Zakładam, że musi on skonstruować nowy obiekt za każdym razem, gdy chce zgłosić postęp.

Nie widzę tu różnicy tak bardzo.

Czy przeoczyłem zupełnie inne techniki raportowania postępów?

Sonduj z przodu w oddzielnym wątku z przyrostami atomowymi w backend. Sondowanie ma tutaj sens, ponieważ dotyczy operacji, która kończy się w określonym czasie, a prawdopodobieństwo, że frontend odbierze zmiany stanu, jest wysokie, szczególnie jeśli dążysz do uzyskania jedwabiście gładkiego paska postępu. Możesz wziąć pod uwagę zmienne warunkowe, jeśli nie podoba ci się pomysł odpytywania z wątku interfejsu, ale w takim przypadku możesz chcieć uniknąć powiadamiania o każdym przyrostowym szczegółowym pasku postępu.


źródło
2

Jest to różnica między mechanizmem powiadomień push i pull .

Zmienny obiekt ( pull ) będzie musiał być wielokrotnie odpytywany przez interfejs użytkownika i synchronizowany, jeśli oczekujesz, że zadanie zaplecza zostanie wykonane w wątku tła / procesu roboczego.

Oddzwonienie ( push ) utworzy pracę dla interfejsu użytkownika tylko wtedy, gdy coś się faktycznie zmieni. Wiele platform interfejsu użytkownika ma również wywołanie invokeOnUIThread z wątku roboczego, aby uruchomić fragment kodu w wątku interfejsu użytkownika, dzięki czemu można faktycznie wprowadzać zmiany bez wchodzenia w zagrożenia związane z wątkiem. (gra słów zamierzona)

Zasadniczo preferowane są powiadomienia wypychane, ponieważ działają one tylko wtedy, gdy trzeba je wykonać.

maniak zapadkowy
źródło
1
Myślę, że ogólnie to, co mówisz, jest słuszne. Jednak w tym konkretnym przypadku, na pasku postępu, zmiany mogą nastąpić szybko. Jeśli spodziewasz się, że „postęp” może się zmieniać wiele razy na sekundę, bardziej sensowne jest użycie modelu pull, ponieważ w przeciwnym razie musisz się martwić, że interfejs użytkownika otrzyma zbyt wiele powiadomień do obsługi.
Gort the Robot
Wysłanie obiektu postępu może ukryć mechanizm powiadamiania, którego używasz z zaplecza, ponieważ być może obiekt postępu wykonuje wywołania zwrotne. Tak naprawdę nigdy nie używałem mechanizmu ciągnącego, o ile pamiętam, i zapomniałem o tym: 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.- Nie, jeśli zmiennym obiektem jest samo okno dialogowe lub działający interfejs do niego. Oczywiście to i tak oznacza oddzwonienie.
Robert Harvey,
1
Co? OP wyraźnie opisuje dwie różne formy mechanizmu pchającego, w żadnym przypadku nie jest konieczne odpytywanie.
Doc Brown
0

Używam websockets z AngularJS. Gdy interfejs odbiera wiadomość, wyświetla ją w wyznaczonym obszarze wiadomości, który po kilku sekundach gaśnie. Z tyłu po prostu wysyłam komunikaty o stanie do kolejki komunikatów. Wysyłam tylko tekst, ale nie ma powodu, dla którego nie mogłem wysłać obiektu statusu z wartościami takimi jak procent ukończenia lub prędkość przesyłania.

TMN
źródło
0

Wspominasz o swoich „dwóch sposobach”, jak gdyby były to odrębne koncepcje, ale chcę się trochę wycofać.

  1. Przekaż zmienny obiekt do zaplecza i niech zaplecze wprowadzi do niego zmiany w miarę postępu. Obiekt powiadamia interfejs użytkownika, gdy nastąpi zmiana.

Powiedziałeś już, że chcesz uniknąć ścisłego powiązania interfejsu użytkownika z logiką, więc mogę spokojnie założyć, że ten „obiekt zmienny”, który przekazujesz, jest w rzeczywistości implementacją określonego interfejsu zdefiniowanego w module logicznym. Jako taki, jest to tylko kolejny sposób przekazania wywołania zwrotnego do procesu, który jest okresowo wywoływany z informacją o postępie.

Co do zalet i wad ...

Wadą metody (1) jest to, że klasa implementująca interfejs może to zrobić tylko raz. (Jeśli chcesz wykonywać różne zadania przy użyciu różnych wywołań, potrzebujesz instrukcji switch lub wzorca gościa). W metodzie (2) ten sam obiekt może używać innego wywołania zwrotnego dla każdego wywołania kodu zaplecza bez potrzeby przełącznik.

Zaletą metody (1) jest to, że o wiele łatwiej jest mieć wiele metod w interfejsie niż radzić sobie z wieloma wywołaniami zwrotnymi metody (2) lub pojedynczym wywołaniem zwrotnym za pomocą instrukcji switch dla wielu kontekstów.

Daniel T.
źródło
-2

Techniki, których możesz użyć, mogą być bardzo różne.

Próbuję rozgryźć inny scenariusz

  • Żądanie do db
  • pobieranie pliku

Proste żądanie logowania do db (średnia odpowiedź z db jednym elemtem) nie wymaga postępu raportu, ale zawsze może odpalić wątek interfejsu użytkownika w oddzielnym zadaniu np. asynchroniczny lub pracujący w tle, tutaj potrzebujesz tylko jednego wywołania zwrotnego dla wyniku.

Ale co, jeśli zapytasz o wszystkie swoje zapasy z 1mln pozycji? To zapytanie powinno zająć kilka minut, dlatego w takim przypadku musisz zaimplementować postępy w perport w logice biznesowej w postaci pozycji / pozycji, a następnie możesz zaktualizować interfejs użytkownika i może pojawić się opcja anulowania oddzwonienia.

To samo dotyczy pobierania pliku. Zawsze możesz zaimplementować tutaj swoje wywołanie zwrotne postępu w postaci bajtu bajtów, i utrzymywać kontrolę nad komunikacją przez http jest bardzo często wzorcem.

W moim osobistym podejściu wdrażam logikę postępu biznesowego tylko dla moich klientów, unikając udostępniania innych obiektów punktowi końcowemu.

Marco Luongo
źródło
1
To tak naprawdę nie odpowiada na pytanie o zalety / wady.
Benni,