我正在尝试添加一个地址可观察对象到用户可观察对象。
export interface Address {
country: string;
state: string;
city: string;
street: string;
zipCode: number;
}
export interface User {
id: number;
name: string;
address?: Address;
}
操作代码为:
// Observables - declaration
let users$: Observable<User[]>;
let address$: Observable<Address[]>;
// Observables - assignation
users$ = of(users);
address$ = of(address);
const getUser = (userId: number) => of(users[userId]);
users$ = address$.pipe(
switchMap((address: Address[]) => {
const userArray$: Observable<User>[] = [];
address.forEach((add, index) => {
const user$: Observable<User> = getUser(index).pipe(
map((user$) => ({...user$, address: add})),
tap(userArray$ => console.log('Alla',userArray$))
)
userArray$.push(user$)
tap(userArray$ => console.log('Allb',userArray$))
})
return forkJoin(userArray$);
})
)
// Subscription
users$.subscribe(() => console.log(users))
若干问题:
- 为什么用户$ subscription没有地址对象?
- 为什么第一个tap执行并显示一个地址对象,而第二个tap似乎没有执行?
- concatMap是合适的映射操作符吗?
这里提供了一个StackBlitz
为什么用户$ subscription没有地址对象?
你的问题中的代码与stackblitz中的代码有一些显著的不同。使用问题中的代码,它确实有一个地址对象。对于stackblitz中的代码,它没有地址对象的原因是你只是使用getUser(index)
而没有修改。您在73上定义了一个函数,看起来像一个映射函数,但您没有将其传递给map
为什么第一个tap执行并显示一个地址对象,而第二个tap似乎没有执行?
第二个tap
在自己的行上,而不是pipe
的一部分。单独调用tap
并没有多大作用。它(和其他操作符)返回你想要修改的可观察对象的行为描述,但是你需要把它传递给.pipe
来创建新的可观察对象。
concatMap是合适的映射操作符吗?
你的例子很简单(所有的源可观察对象都是同步的,并且只发出一个值),switchMap、concatMap和mergeMap都将有相同的结果。对于更复杂的情况(多个值在时间上分散),区别如下:
concatMap:当源可观察对象发出第一个值时,我们开始处理第一个映射的可观察对象。第一个可观察对象必须完成后,我们才能继续处理源可观察对象的第二个值。当您需要保证所有事情都按照精确的顺序发生,并且没有任何事情提前终止时,concatMap非常有用。但是如果你有一个永不结束的可观察对象,这将阻止其他可观察对象的运行。
switchMap:当发出第一个值时,我们开始处理第一个映射的可观察对象。只要源中没有更多的值,这个过程就会持续下去,但是一旦从源中发出了一个新值,我们就会取消第一个映射的可观察对象,并切换到第二个。当一个新值表示我们需要停止工作重新开始时,switchMap是很有用的。
mergeMap:任何时候发出一个值,我们也会处理它映射的可观察对象。值将由多个映射的可观察对象以它们碰巧到达的顺序发出,并且不会取消任何内容。当您不关心顺序,只想按照它们到达的顺序尽快处理它们时,mergeMap非常有用。
基于您更新的Stackblitz,您没有获得最终observable
中包含的地址的原因是因为您没有在subscribe
//before
users$.subscribe((/*no Data from the source*/) => console.log(users))
//after
users$.subscribe((withAddress) => console.log(withAddress))