How do I simplify this sequence, as I seem to be re-creating the pyramid of doom, and there ought to be a more Rx-y way.
public isEligibleForOffers(): Observable<Boolean> {
return Observable.create((observer) => {
this.getAccounts().subscribe(
(accounts) => {
if (!this.accountsAreInCredit(accounts)) {
observer.next(false);
} else {
this.getOffers(accounts).subscribe(
(data: Offers) => {
let isEligible = (data.eligible && this.eligibleForAny(data) === true && this.registeredForAny(data) !== true);
observer.next(isEligible);
}
);
}
});
});
}
I need to make an XHR call to get a collection of accounts, and if the accounts are in credit, make another XHR call to get current offers, and if the user is eligible for any offer return true otherwise false.
Essentially
The difference between what I'm asking and what I've seen on SO is twofold:
Another similar case might be (1) Get a token, then (2) use the token in a subsequent request.
If you want to chain the observables, use switchMap
or flatMap
. Also, you do not need to purposely create another Observables using Observable.create()
, since your this.getAccounts()
is already returning an observable.
This should be more succinct:
public isEligibleForOffers(): Observable<Boolean> {
return this.getAccounts().switchMap(accounts => {
if (this.accountsAreInCredit(accounts)) {
return this.getOffers(accounts)
.map((data: Offers) => {
return (data.eligible && this.eligibleForAny(data) === true && this.registeredForAny(data) !== true);
})
}
//you can be explicit by returning a an Observable of false:
return Obersvable.of(false);
})
}
And you can just use it like this:
this.isEligibleForOffers()
.subscribe(flag => {
//flag is a boolean
if(flag){
//do something
}else{
//do something else
}
})
Given signatures:
getAccounts(): Account[]
accountsAreInCredit(accounts: Account[]): boolean
getOffers(accounts: Account[]): Offers
You could model your function as follows:
public isEligibleForOffers(): Observable<Boolean> {
return this.getAccounts()
.filter(accounts => this.accountsAreInCredit(accounts))
.mergeMap(
accounts => this.getOffers(accounts),
(accounts, offers) => offers.eligible && this.eligibleForAny(offers) && !this.registeredForAny(offers))
)
.concat(Rx.Observable.of(false))
.take(1);
}
So if accountsAreInCredit
yields false the stream will become empty and we then use the .concat
operator to append a default value of false
to the stream.
The mergeMap
(aka flatMap
) takes an optional argument resultSelector
in which you can map the output values so we map them to the isEligibleForOffers
boolean value.
Then finally by limiting the amount of values the function isEligibleForOffers
can produce we prevent ourselves from emitting true
(from the mergeMap
) and false
(the default from the concat
) as emissions.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.