Baza wiedzy
Wyobraźmy sobie sytuację, gdy musimy wysyłać żądania do backendu w sposób synchroniczny, tj. wysyłamy żądanie nr 1 i dopiero po nadejściu odpowiedzi z serwera wysyłamy kolejne żądanie nr 2. Nie znalazłem żadnej opcji w Angularze lub rxjs, która by w prosty sposób realizowała taki scenariusz. Trzeba więc zbudować takie rozwiązanie samodzielnie.
Zanim zacznę przedstawiać poszczególne etapy wraz z fragmentami kodu, od razu wspomnę o potrzebnych importach:
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';
Brak tych importów nie będzie przeszkadzał podczas kompilacji, dopiero uruchomienie kodu będzie skutkować błędami typu "nieznana metoda X".
Na początek spróbujemy sprawdzić możliwości biblioteki rxjs. Jak wiemy z poprzedniego wpisu biblioteka ta ma wiele przydatnych klas, z których jedna przyda nam się do rozwiązania naszego problemu. To klasaSubject
, która dostarcza przede wszystkim metodęnext
, za pomocą której będziemy wysyłać poszczególne żądania.
Spróbujmy napisać kawałek kodu z metodą, która ma za zadanie wysyłać żądania GET w sposób opisany na początku. Metoda powinna na liście parametrów url żądania i zwracać obiektObservable
, który można "zasubskrybować":
private syncSubject = new Subject<any>();
constructor(private http: HttpClient) {
this.syncSubject.subscribe(url => {
http.get(url);
});
}
public sendGetSync(url: string): Observable {
this.syncSubject.next(url);
}
Widzimy tworzenie prywatnego pola oraz jego subskrypcję w konstruktorze, w którym będziemy wysyłać kolejno pojawiające się URL-e. Widzimy też metodęsendGetSync
, która wywołujenext
. Jak na razie ten kod się przede wszystkim nie kompiluje, bo metoda nie zwraca wartości. Dodatkowo, gdybyśmy tam jednak wstawili zwracanie np. null-a, to i tak to rozwiązanie nie spełnia naszego wymagania – przede wszystkim kolejne wywołania będą wywoływane natychmiastowo, ponieważ funkcja będąca argumentem metodysubscribe
jest wywoływana jak najbardziej asynchronicznie – nie jest to w żaden sposób skolejkowane. Rozwiążmy najpierw ten drugi problem.
Użyjmy metodymergeMap
. Przyjmuje ona 3 parametry, z czego interesują nas tylko pierwszy i trzeci. Pierwszy to "projekcja" czyli funkcja, która zamienia wchodzącą wartość na obiektObservable
(dokładnie rzecz biorąc spodziewa się typu ogólniejszego:ObservableInput
). Trzeci parametr to liczba jednoczesnych przetworzeń – tutaj damy wartość 1, co spowoduje kolejkowanie naszych żądań.
constructor(private http: HttpClient) {
this.syncSubject.mergeMap(url => {
return http.get(url);
}, null, 1).subscribe();
}
Zwróć uwagę, że metodasubscribe
pozostała pusta, a faktyczne wywołanie żądania zostało przeniesione domergeMap
, co dodatkowo jest dla nas korzystne, bo wywołaniehttp.get
zwracaObservable
, czyli wymagany typ zwracanej wartości funkcji z pierwszego parametru.
Pozostaje nam jeszcze problem wartości zwracanej przez metodęsendGetSync
. Chcielibyśmy w odpowiedzi otrzymać obiektObservable
, który zawiera odpowiedź z rzeczywistego wywołania http.get
. Nie mamy do tego dostępu, bo wywołanienext
nie zwraca żadnej wartości.
Utwórzmy pomocniczy interfejs:
interface GetRequestData {
url: string;
resultSubject: Subject<any>;
}
Przeróbmy definicję naszego pola:
private syncSubject = new Subject<GetRequestData>();
Przeróbmy metodęsendGetSync
:
public sendGetSync(url: string): Observable<any> {
const resultSubject = new Subject<any>();
this.syncSubject.next({url, resultSubject});
return resultSubject;
}
Widzimy tutaj, że tworzymy nowySubject
, następnie konstruujemy obiekt typuGetRequestData
, do którego przekazujemy URL-a oraz stworzony przed chwiląSubject
. I dopiero tak stworzony obiekt podajemy do wywołania metodynext
.
Przeróbmy ostatecznie wywołaniemergeMap
:
constructor(private http: HttpClient) {
this.syncSubject.mergeMap(getRequestData => {
return http.get(getRequestData.url).do(result => {
getRequestData.resultSubject.next(result)
});
}, null, 1).subscribe();
}
Zmiana przede wszystkim polega na tym, że wynik wywołania http.get
używamy jako parametr do wywołania metodynext
na obiekcieSubject
, który stworzyliśmy w metodziesendGetSync
. Wywołanie to wykonujemy w ramach wywołania metodydo
– dlaczego? Metodado
działa trochę jak peek w kolejkach, czyli sprawdzamy wartość w kolejce, ale jej nie wyjmujemy. "Wyjęcie wartości z kolejki" następuje w widocznym na końcu wywołaniusubscribe
.
Voilà – to powinno już działać. Dla porządku powinniśmy jeszcze uwzględnić obsługę błędów, metoda do
ma taką możliwość:
constructor(private http: HttpClient) {
this.syncSubject.mergeMap(getRequestData => {
return http.get(getRequestData.url).do(
result => getRequestData.resultSubject.next(result),
error => getRequestData.resultSubject.error(error)
).catch(err => Observable.of(null));
}, null, 1).subscribe();
}
Wywołanie catch
ma zapobiec sytuacji, w której błąd powstający w wywołaniuhttp.get
mógłby spowodować "wyłożenie się" naszego obiektu syncSubject
. A w konsekwencji nasz mechanizm synchronizacyjny przestałby po prostu działać.
Przedstawiony w tym materiale problem z wysyłaniem żądania do serwera można rzecz jasna rozszerzyć o podobne sytuacje, w których mamy do czynienia z asynchronicznym kodem.
Zainteresował Cię ten wpis?
Chcesz dowiedzieć się więcej?
Michał Gierwatowski
Programista wszechstronny, od języka Progress4GL począwszy, przez Javę, na TypeScripcie kończąc. Ponad piętnastoletnie doświadczenie w wytwarzaniu różnego rodzaju systemów informatycznych. Ostatnio interesuje się nowinkami w ekosystemie JavaScript/node.js
Michał.Gierwatowski(at)monolit-it.pl
Zobacz wszystkie artykuły danego autora »Ostatnie:
Najpopularniejsze TAGi:
Tagi
W swoim czasie linia produktowa stacjonarnych komputerów biznesowych Dell OptiPlex dzieliła się na kilka różnych modeli, a te z kolei podzielono na dodatkowe modele w danej linii produktowej opartej na niezbyt jasnej numeracji. Dla osoby nie obeznanej z nazewnictwem był to spory problem aby odnaleźć się w gąszczu dostępnych wersji.
O rozwiązaniach IoT pisze się najczęściej w kontekście przemysłu 4.0, czy inteligentnych miast. Rozwiązanie NetQM for IoT jest z sukcesem wdrażane w jednostkach samorządu terytorialnego jako System Monitorowania i Sterowania Siecią Kanalizacji Ciśnieniowej.
Firma Dell od samego początku charakteryzowała się dużą dbałością w kwestii zakresu gwarancyjnego dla swoich produktów. Nie inaczej wygląda sytuacja dziś i można zaryzykować stwierdzenie, że jest to niejako wyróżnik tej organizacji na tle jej konkurencji.
W ostatnim czasie pojawiło się kilka ciekawych rozwiązań zastosowanych przez producentów notebooków. Większość z nich jest jak najbardziej dostosowana do potrzeb potencjalnych użytkowników i wydaje się, że trafi prosto pod strzechy.
Od dawna mówi się o usługach czy przemyśle 4.0 w kontekście biznesu. Coraz częściej jednak rozmawia się również o usługach 4.0 skierowanych do samorządów... Przykład? System monitorowania i sterowania siecią kanalizacji niskociśnieniowej w oparciu o rozwiązanie NetQM for IoT.
Miniony rok, podobnie jak poprzednie lata, cechował się dużą zmiennością i zawirowaniami w różnych sferach naszego życia, w tym między innymi dotyczącymi cyberbezpieczeństwa. Znana firma analityczno-badawcza jaką jest Gartner, wykazała siedem głównych trendów związanych z bezpieczeństwem IT w roku 2022.
Bezpieczeństwo odmieniane przez wszystkie przypadki jest obecnie jedną z najczęściej podnoszonych kwestii w świecie IT. Dodatkowo w erze postpandemicznej, która spowodowała przejście do pracy zdalnej lub hybrydowej, doszło do decentralizacji ludzi i maszyn, a co za tym idzie, wzmogło konieczność skutecznej kontroli dostępu uprzywilejowanego.
Jeden z naszych klientów zwrócił się do nas w sprawie przedstawienia propozycji zakupu urządzeń, które umożliwiłyby przekształcenie w formę cyfrową jego bardzo dużych zasobów bibliotecznych.
Wzmocnione notebooki czy tez tablety to dosyć specyficzny wycinek rynku IT. Sprzęt ten często charakteryzuje się gorszymi parametrami technicznymi niż topowe produkty (choć to tez powoli ulega zmianie), ale ma coś czego typowo biznesowy sprzęt nie ma i mieć nie będzie.