Reputation: 862
I have been working with a convention where my functions return observables in order to achieve a forced sequential series of function calls that each pass a returned value to their following "callback" function. But After reading and watching tutorials, it seems as though I can do this better with what I think is flatmap. I think I am close with this advice https://stackoverflow.com/a/34701912/2621091 though I am not starting with a promise. Below I have listed and example that I am hoping for help in cleaning up with advice on a nicer approach. I am very grateful for help you could offer:
grandparentFunction().subscribe(grandparentreturnobj => {
... oprate upon grandparentreturnobj ...
});
grandparentFunction() {
let _self = this;
return Observable.create((observer) => {
...
_self.parentFunction().subscribe(parentreturnobj => {
...
_self.childFunction( parentreturnobj ).subscribe(childreturnobj => {
...
observer.next( grandparentreturnobj );
observer.complete();
});
});
});
}
parentFunction() {
let _self = this;
return Observable.create((observer) => {
...
observer.next( parentreturnobj );
observer.complete();
}
}
childFunction() {
let _self = this;
return Observable.create((observer) => {
...
observer.next( childreturnobj );
observer.complete();
}
}
Upvotes: 3
Views: 2119
Reputation: 461
The general rule-of-thumb in RxJS is that you should really try to avoid creating hand-made, custom Observables (i.e., using Observable.create()
) unless you know what you're doing, and can't avoid it. There are some tricky semantics that can easily cause subtle problems if you don't have a firm grasp of the RxJS 'contract', so it's usually better to try to use an existing Observable creation function. Better yet, create Observables via applying operators on an existing Observable, and return that.
In terms of specific critiques of your example code, you're right that you should be using .flatMap()
to create Observable function chains. The nested Observable.create()
s you currently have are not very Rx-like, and suffer from the same problems 'callback hell'-style code has.
Here's an example of doing the same thing your example does, but in a more idiomatic Rx style. doStuff()
is our asynchronous function that we want to create. doStuff()
needs to call the asynchronous function step1()
, chain its result into the asynchronous function step2()
, then do some further operations on the result, and return the final result to doStuff()
's caller.
function doStuff(thingToMake) {
return step1(thingToMake)
.flatMap((step1Result) => step2(step1Result))
.map((step2Result) => {
let doStuffResult = `${step2Result}, and then we're done`;
// ...
return doStuffResult;
});
}
function step1(thingToMake) {
let result = `To make a ${thingToMake}, first we do step 1`;
// ...
return Rx.Observable.of(result);
}
function step2(prevSteps) {
let result = `${prevSteps}, then we do step 2`
// ...
return Rx.Observable.of(result);
}
doStuff('chain').subscribe(
(doStuffResult) => console.log(`Here's how you make a chain: ${doStuffResult}`),
(err) => console.error(`Oh no, doStuff failed!`, err),
() => console.debug(`doStuff is done making stuff`)
)
Rx.Observable.of(x)
is an example of an existing Observable creator function. It just creates an Observable that returns x
, then completes.
Upvotes: 2