[英]Rxjs - remap object with observables as values
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.似乎没有多少人知道 *Map 操作符曾经有一个叫做resultSelector
的东西作为它们的第二个参数。 Now in rxjs v6, you can do the same with inner map
, let me show you.现在在 rxjs v6 中,你可以对inner map
做同样的事情,让我告诉你。
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 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 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.他们上面的方式(使用Observable.of
)本质上是创建一个由低级 Observable 组成的高级 Observable。
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:我认为更好的运算符是forkJoin
,因为这些 Observable 中的每一个都是冷的和有限的,并且forkJoin
捕获每个 Observable 的第一次发射并在所有值完成时发射所有值:
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.请注意, forkJoin
发出的项将是一个数组,其索引与传入的 Observable 的索引相匹配。
If the source object already contains Observables you can do it for example like the following (there are obviously multiple ways to do this):如果源对象已经包含 Observables 你可以像下面这样(显然有多种方法可以做到这一点):
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. scan
收集所有响应并将它们合并到一个对象中。
Live demo: https://stackblitz.com/edit/rxjs-ffza8b现场演示: https ://stackblitz.com/edit/rxjs-ffza8b
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.