I'm a stuck in nested observable hell and could do with a hand.
I have the following block of code
return this.findUser(term).map( users => {
return users.map( user => this.getLastLogin(user.user_id).map( last_login => {
user.last_login = last_login;
return user;
}));
});
findUser
returns Observable<User[]>
and getLastLogin
returns Observable<number>
.
I'm basically hoping to fetch a list of users and then update this with the information from another value.
Right now the code above is returning <Observable<Observable<User>[]>
.
I thought I could replace the initial map
with flatMap
but this turns the object into <Observable<Observable<User>>
.
The RxJS documentation is a little hard to decipher so I'm not sure what combination of switch
, forkJoin
or flatMap
will get me to what I need.
I'm hoping to return Observable<User[]>
. Could anyone point me in the right direction?
You can use concatAll() or mergeAll() without any parameter. This (including mergeMap ) works only in RxJS 5+ because it treats Observables, arrays, array-like objects, Promises, etc. the same way.
Flattening operators come to our rescue when we have a nested subscription i.e subscribing to an observable within another subscription. This can be pretty annoying to track and debug. Its similar to “Callback hell” scenario where we have nested callbacks.
Higher-order Observableslink. Observables most commonly emit ordinary values like strings and numbers, but surprisingly often, it is necessary to handle Observables of Observables, so-called higher-order Observables.
Actually, you don't need forkJoin()
nor switch()
to do this.
In general, you want to update each user in the array of users by another async call.
I'd do it like this:
var source = findUser('term')
.mergeAll()
.mergeMap(user => getLastLogin(user.user_id)
.map(last_login => {
user.last_login = last_login;
return user;
})
)
.toArray();
source.subscribe(val => console.log(val));
Operator mergeAll()
converts a higher-order Observable into single observables. In this case it takes the array of all users and re-emits them one by one. Then mergeMap()
emits users updated with the last_login
date. At the end I used toArray()
to transform single users into one large array that is them emitted as whole (you can remove this operator if you want to emit single users instead).
Note that when you used return users.map(...)
you were using Array.map()
that returns an array and not map()
from RxJS that returns an Observable. I think working with single objects is usually easier that with arrays of objects.
See live demo: https://jsbin.com/naqudun/edit?js,console
This prints to console:
[ { name: 'foo',
user_id: 42,
last_login: 2016-11-06T10:28:29.314Z },
{ name: 'bar',
user_id: 21,
last_login: 2016-11-06T10:28:29.316Z } ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With