RxJS - How to replace a Callback in a Subscription with an Observable


RxJS - How to replace a Callback in a Subscription with an Observable



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?





In your first example callback will be called regardless whether the request was successful or not. Is that intended? If it is your second example doesn't make much sense.
– a better oliver
Jul 2 at 10:07


callback





No, that is not intended. It was a bug in my colleagues implementation. The callback should only be called if the request was successful. I will fix it in my question.
– Shamshiel
Jul 2 at 10:20




2 Answers
2



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.


observable.of(true / false)


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.



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.


Promise


Observable


map


true


map


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;
}





I'm not sure why I didn't just return the pipe... I know that the map function is for changing data and passing the change to the next operator but I don't want the data to leave my 'doSomething' function. I looked at the documentation and I'm not sure what other operator is better suited for that.
– Shamshiel
Jul 2 at 6:21






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Rothschild family

Cinema of Italy