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:
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.
Przekaż funkcję zwrotną formularza
void f(ProgressObject)
lubProgressObject -> unit
wywołaną przez back-end. W tym przypadku back-end konstruujeProgressObject
i 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?
źródło
BackgroundWorker
tego, o którym wspomina RH. Zawinięte w niestandardową klasę wraz z „formularzem postępu” itp. Oraz prostym mechanizmem komunikowania wyjątku - jakBackgroundWorker
z 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.Odpowiedzi:
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.
Nie widzę tu różnicy tak bardzo.
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
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ć.
źródło
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.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.
źródło
Wspominasz o swoich „dwóch sposobach”, jak gdyby były to odrębne koncepcje, ale chcę się trochę wycofać.
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.
źródło
Techniki, których możesz użyć, mogą być bardzo różne.
Próbuję rozgryźć inny scenariusz
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.
źródło