Issue
I was ask to add a functionnality to a legacy code. The legacy code handle a get request with some added features like avoiding duplciate calls and cache get request when PWA is offline.
In the context of this PWA when have some POST/PUT/DELETE operation I want to commit before any get is executed.
I made an observable $processingOfflineOperation which return true or false and I want to delay any get operation until $processingOfflineOperation next to false.
this is the get method :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
const pendingObs = this.checkIfPendingToAvoidDuplicateCalls('GET ' + endpoint, params);
if (!!pendingObs) {
// return duplicate call
return pendingObs;
}
if (!reqOpts) {
reqOpts = {
params: new HttpParams()
};
}
let header = new HttpHeaders({Authorization: this.getAuthorizationHeaderValue()});
header = header.set('Content-type', 'application/json');
reqOpts.headers = header;
endpoint = (endpoint) ? endpoint : '';
if (!this.network.online && !avoidCache) {
// return from cache
return of(this.cacheCalls['GET ' + endpoint + ' ' + JSON.stringify(params)]);
} else if (!this.network.online) {
return throwError(() => new Error('No internet'));
}
const $obs = this.http.get(this.url + endpoint + '?' + this.JSON_to_URLEncoded(params), reqOpts).pipe(
catchError((err) => {
this.clearPendingCall('GET ' + endpoint, params);
if (!!ignoreHTTPStatusCode && !!ignoreHTTPStatusCode.find((status) => status === err.status)) {
return of(null);
}
this.manageError(err);
return throwError(err);
}),
map(res => {
this.clearPendingCall('GET ' + endpoint, params);
this.addCacheCall('GET ' + endpoint + ' ' + JSON.stringify(params), res);
return res;
}),
share()
);
this.setPendingCall('GET ' + endpoint, params, $obs);
return $obs;
}
I have no idea how to proceed all my attempt ended changing the behaviour of the get method
My best guess was this but this is not working as excpected :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
const $subject = new Subject();
this.$processingOfflineOperation.subscribe(isProcessing => {
if (isProcessing) {
return ;
}
const pendingObs = this.checkIfPendingToAvoidDuplicateCalls('GET ' + endpoint, params);
if (!!pendingObs) {
// return duplicate call
$subject.next(pendingObs);
}
if (!reqOpts) {
reqOpts = {
params: new HttpParams()
};
}
let header = new HttpHeaders({Authorization: this.getAuthorizationHeaderValue()});
header = header.set('Content-type', 'application/json');
reqOpts.headers = header;
endpoint = (endpoint) ? endpoint : '';
if (!this.network.online && !avoidCache) {
// return from cache
$subject.next(this.cacheCalls['GET ' + endpoint + ' ' + JSON.stringify(params)]);
} else if (!this.network.online) {
$subject.error(new Error('No internet'));
return throwError(() => new Error('No internet'));
}
const $obs = this.http.get(this.url + endpoint + '?' + this.JSON_to_URLEncoded(params), reqOpts).pipe(
catchError((err) => {
this.clearPendingCall('GET ' + endpoint, params);
if (!!ignoreHTTPStatusCode && !!ignoreHTTPStatusCode.find((status) => status === err.status)) {
return of(null);
}
this.manageError(err);
$subject.error(err);
return throwError(err);
}),
map(res => {
this.clearPendingCall('GET ' + endpoint, params);
this.addCacheCall('GET ' + endpoint + ' ' + JSON.stringify(params), res);
$subject.next(res);
}),
share()
);
this.setPendingCall('GET ' + endpoint, params, $obs);
$obs.subscribe();
});
return $subject;
}
Following @dmance comment I tried to rename the first version of the get into getReal and created a new function "get" then I did this :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
return this.getReal(endpoint, params, avoidCache, reqOpts, ignoreHTTPStatusCode).pipe(
filter(() => !this.isProcessing),
take(1)
);
}
but still no chance,
then this :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
const $subject = new Subject();
this.getReal(endpoint, params, avoidCache, reqOpts, ignoreHTTPStatusCode).pipe(
skipUntil(this.$processingOfflineOperation.pipe(filter(isProcessing => {
return isProcessing === false;
})))
).subscribe({
next: (v) => {
$subject.next(v);
},
error: (err) => {
$subject.error(err);
}
});
return $subject;
}
not working either.
Not sure this is the most elegant way to do it, but this one works :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
const $subject = new Subject();
this.$processingOfflineOperation.subscribe((isProcessing) => {
if (!isProcessing) {
this.getReal(endpoint, params, avoidCache, reqOpts, ignoreHTTPStatusCode).subscribe({
next: (v) => {
$subject.next(v);
},
error: (err) => {
$subject.error(err);
}
});
}
});
return $subject;
}
Solution
Not sure this is the most elegant way to do it, but this one works :
public get(endpoint: string, params?: any, avoidCache?: boolean, reqOpts?: any, ignoreHTTPStatusCode?: HttpStatusCode[]): Observable<any> {
const $subject = new Subject();
this.$processingOfflineOperation.subscribe((isProcessing) => {
if (!isProcessing) {
this.getReal(endpoint, params, avoidCache, reqOpts, ignoreHTTPStatusCode).subscribe({
next: (v) => {
$subject.next(v);
},
error: (err) => {
$subject.error(err);
}
});
}
});
return $subject;
}
Elegant
You should be able to re-write this without an intermediate Subject. I can’t test this for you, but something like this should work:
public get(
endpoint: string,
params?: any,
avoidCache?: boolean,
reqOpts?: any,
ignoreHTTPStatusCode?: HttpStatusCode[]
): Observable<any> {
return this.$processingOfflineOperation.pipe(
mergeMap(isProcessing => isProcessing ? EMPTY :
this.getReal(endpoint, params, avoidCache, reqOpts, ignoreHTTPStatusCode)
)
);
}
You’ll notice that by not managing your own subscriptions, the code becomes much shorter/concise. In general, nested subscribe
blocks are a code smell. There’s most often a better way.
Answered By – Mrk Sef
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0