2014-12-03 8 views
8

Buduję aplikację Firemonkey w programie Rad Studio XE7, w której przy jednym kliknięciu przycisku będę musiał wykonywać wiele (około 7) wywołań usług sieciowych za pomocą TRestRequest. Każda usługa WWW zwróci obiekt Json, który następnie zapełni zestaw danych. Szukam sposobu na jednoczesne wywoływanie tych połączeń i blokowanie interfejsu użytkownika aplikacji.Wiele żądań REST równolegle z FireMonkey

Który sposób zaleca się zrobić? Widziałem, że Embarcadero wprowadziło nowe funkcje zadań i funkcji do wątków, ale nadal nie jestem pewien, czy możemy tego użyć i jak. Również, widziałem, że istnieje funkcja wykonać TRestRequest asynchronicznie, przy użyciu tej funkcji:

function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; AFreeThread: boolean = true): TRESTExecutionThread; 

ale nie mogę znaleźć żadnej dokumentacji na temat korzystania z tej metody i co robi.

Wszelkie dane wejściowe będą bardzo mile widziane.

+0

@JerryDodge nie oznacza, że ​​Async nie będzie blokował? Ostatecznie operacja asynchronizacji zostanie zakończona, a aplikacja może zrobić coś z wynikiem. – mjn

+0

@mjn To jedno kliknięcie, tam gdzie będziemy je wywoływać, 7 serwisów internetowych otworzy w rzeczywistości multiview, który w naszym przypadku będzie zawierał pakiety comboboxów filtrujących, które będą zapełniane przez pobieranie danych z usług sieciowych. Mój pomysł polegał na umożliwieniu użytkownikowi, aby rzeczywiście anulował to filtrowanie i po prostu wrócił, dotykając obszaru poza widokiem multiview, który zamknie widok wielu i nie zastosuje żadnych filtrów. Pomysł na wiele równoległych żądań był taki, że uważamy, że te 7 comboboxów zostanie szybko zapełnione, jeśli wyślemy 7 żądań równolegle zamiast wysyłać jedno żądanie naraz. – Emulic

+0

@JerryDodge Powinieneś wykonywać ** wszystkie ** długie (er) uruchomione zadania w tle, aby zapobiec blokowaniu wątku interfejsu użytkownika. Jest to nie tylko dla użytkownika, ale także system operacyjny szuka nieodpowiadających aplikacji. –

Odpowiedz

2

Metoda jest naprawdę droga. Jak sama nazwa wskazuje, jest asynchroniczna; co oznacza, że ​​Twój program pozostaje responsywny po wystrzeleniu żądania lub nawet po wystrzeleniu kilku próśb.
Jednak ważne jest, aby wywoływać te różne żądania z różnych instancji obiektu; jeśli uruchomisz instancję ExecuteAsync z instancji TRESTRequest, która już wykonuje operację ExecuteAsync, nowe żądanie stanie się przeszkodą w istniejącym żądaniu. Musisz utworzyć oddzielną instancję TRESTRequest dla każdego połączenia równoległego.

Należy zauważyć, że jego pierwszym parametrem jest procedura; możesz podać wybraną procedurę jako ten parametr. Jedynym wymogiem jest to, że procedura ma prawidłowy podpis; w tym przypadku, że jest to procedura, która nie ma parametrów.

Metoda ExecuteAsync zdefiniowana jest w REST.Client. (Mam Delphi XE-10.1, Berlin, więc ma jeden dodatkowy parametr, ACompletionHandlerWithError - który jest wywoływany przy błędzie. Zasada pozostaje jednak taka sama).

Rzućmy okiem:

function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; 
    AFreeThread: boolean = true; ACompletionHandlerWithError: TCompletionHandlerWithError = nil): TRESTExecutionThread; 
begin 
    Result := TRESTExecutionThread.Create(Execute, self, ACompletionHandler, ASynchronized, AFreeThread, ACompletionHandlerWithError); 
end; 

Co się dzieje, jest to, że nowy wątek jest tworzony w którym wniosek reszta jest wykonywana. Jeśli odpowiedź przychodzi, jest obsługiwana przez ACompletionHandler. Domyślnie ACompletionHandler uruchamia nowy wątek utworzony przez ExecuteAsync. Jeśli chcesz, aby działał na głównym wątku, powinieneś ustawić ASynchronized na true.

Ale jak uzyskać dostęp do tej odpowiedzi i udostępnić ją reszcie programu?

Klasa TRESTRequest programu FireMonkey ma właściwość Response, która odnosi się do obiektu TRESTResponse, który zawiera odpowiedź serwera na nasze żądanie.
Niestety, ani obiekt TRESTRequest, ani obiekt TRESTResponse nie są przekazywane do naszego narzędzia CompletionHandler!

Musimy więc jakoś przekazać tę informację do CompletionHandler. Na szczęście możemy użyć metody jako CompletionHandler.

Załóżmy, że klasa, która będzie przetwarzać wynikowe dane, nazywa się DataOwner.Naszym celem jest, aby DataOwner miał dostęp do instancji TRESTResponse powiązanej z naszą instancją TRESTRequest.
Najprostszym sposobem zrobienia tego jest sprawienie, że TRESTRequest i/lub TRESTResponse są członkami DataOwner.

Załóżmy swoją prośbę został zwolniony z instancji TRESTRequest nazwie MyRESTRequest i przetwarzane przez funkcję processResponse, należy użyć następującego kodu:

type TDataOwner = class 
    MyData: TSomeDataType; 
    procedure GetData(); 
    procedure FillDataSet(); 
end; 

implementation 

procedure DataOwner.GetData(); 
begin 
    // ... initialize MyRESTRequest here... 
    MyRESTRequest.ExecuteAsync(FillDataSet); 
end; 

procedure DataOwner.FillDataSet(); 
begin 
    MyData := processResponse(MyRESTRequest.Response); 
end; 

Jeśli chcesz odpalić kilka wniosków równolegle, trzeba użyć ten wzór kilka razy. Szkoda, że ​​nie mamy bezpośredniego dostępu do instancji TRESTRequest lub instancji TRESTResponse w naszych programach obsługi, ponieważ oznacza to, że sami musimy prowadzić księgowość. Innymi słowy, to do programisty należy upewnienie się, że program obsługi zakończenia przetwarza właściwy obiekt odpowiedzi.

Powiązane problemy