I got an observable like this:
const original = Observable.of({
a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
});
I need to resolve the inner observables and get something like this in the subscribe handler:
{
a: ResponseFromServer,
b: ResponseFromServer,
c: ResponseFromServer,
}
How should I approach this problem?
Thanks.
EDIT: I've figured it out, read below.
It seems that not many people know that *Map operators used to have something called resultSelector
as their second argument. Now in rxjs v6, you can do the same with inner map
, let me show you.
const original = Observable.of({
a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
});
const transformed = original.pipe(
mergeMap(sourceValue =>
forkJoin(_.values(sourceValue)).pipe(map(resolvedHttpRequests => {
// here you have access to sourceValue as well as resolvedHttpRequests so you can do manual join to match the data.
}))
)
)
Update for 2020
forkJoin(
// as of RxJS 6.5+ we can use a dictionary of sources
{
google: ajax.getJSON('https://api.github.com/users/google'),
microsoft: ajax.getJSON('https://api.github.com/users/microsoft'),
users: ajax.getJSON('https://api.github.com/users')
}
)
// { google: object, microsoft: object, users: array }
.subscribe(console.log);
https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin
They way you're doing it above (with Observable.of
) is essentially creating a higher-level Observable of lower Observables.
I think a better operator would be forkJoin
, as each of these Observables are cold and finite and forkJoin
captures the first emission of each Observable and emits all values when all are complete:
const original = forkJoin([
this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
this.http.get('https://jsonplaceholder.typicode.com/todos/33')
]);
let result = {a: null, b: null, c: null};
original.subscribe([a,b,c] => result = {a,b,c});
Just note that the emitted item from forkJoin
will be an array whose index match the indices of the Observables passed in.
If the source object already contains Observables you can do it for example like the following (there are obviously multiple ways to do this):
const mockXHRCall = (url: string) => {
return of('response');
};
const original = of({
a: mockXHRCall('https://jsonplaceholder.typicode.com/todos/11'),
b: mockXHRCall('https://jsonplaceholder.typicode.com/todos/22'),
c: mockXHRCall('https://jsonplaceholder.typicode.com/todos/33')
}).pipe(
mergeMap(obj => {
const observables$ = Object.keys(obj).map(key => obj[key].pipe(
map(response => (
{ [key]: response } // wrap the response with eg. { c: ResponseFromC }
)),
));
return merge(...observables$);
}),
scan((acc, wrapped) => (
{ ...acc, ...wrapped }
), {}),
takeLast(1),
).subscribe(console.log);
The scan
collects all responses and merges them into a single object.
Live demo: https://stackblitz.com/edit/rxjs-ffza8b
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.