Reputation: 2211
Recently a colleague of mine implemented a callback in a Subscription in our Angular 6 application like this:
callDoSomething() {
this.doSomething(() => {
// logic from callback
});
}
public doSomething(callback: any) {
this.http.get('/api/doSomething').subscribe(data => {
// do something with data
if (callback) {
callback();
}
}, error => {
// do something with error
});
}
I thought it would be better to solve this "problem" with an RxJS Observable instead. But after I implemented/refactored the code it doesn't seem to be the better solution. The code looks much more complicated now. Is it necessary to cast the pipe to a promise and return it as an observable?
import { Observable, of as observableOf, from as observableFrom } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
callDoSomething() {
this.doSomething().subscribe((result) => {
if (result) {
// logic from callback
}
});
}
public doSomething() {
let promise = this.http.get('/api/doSomething').pipe(
map(data => {
// do something with data
return observableOf(true);
}),
catchError((error) => {
// do something with error
return observableOf(false);
})
).toPromise()
return observableFrom(promise);
}
Is there a better way to solve this problem with Observables? Or was my colleague right to use a simple callback?
Upvotes: 0
Views: 4196
Reputation: 26828
IMHO the callback is the simplest solution. The complete handler would probably be a better place to call it. If you want to use observables then I would adhere to the stream semantics.
If your goal is to handle the success case and you don't want to get data out of the function then you can return an empty, completed observable. I case of an error you can either let it pass through or replace it with an empty observable, that terminates with an error. It is similar to observable.of(true / false)
, but relies on the stream semantics instead of arbitrary values.
doSomething() {
return this.http.get('/api/doSomething').pipe(
flatMap(data => {
// do something with data
return empty();
}),
catchError((error) => {
// do something with error
return throwError(error); //instead of error pass whatever you like
})
);
}
callDoSomething() {
this.doSomething()
.subscribe(
undefined, //we don't handle data
undefined, //we don't handle errors
() => {// logic from callback}
);
}
As you can see it's still much more code than in the callback solution. But if you add an error callback one day it might be an interesting option. The upside of the observable solution: No ifs.
Upvotes: 2
Reputation: 68655
You don't need to cast to Promise
and then again cast into Observable
. Also look at the map
function. Why you return true
from it ? map
function is designed for changing coming data and pass changed ones next.
public doSomething() {
let observable= this.http.get('/api/doSomething').pipe(
map(data => {
// do something with data
return observableOf(true);
}),
catchError((error) => {
// do something with error
return observableOf(false);
}));
return observable;
}
Upvotes: 1