为了有效地处理嵌套订阅,您应该使用“Higher Order Mapping Operator”之一,它可以为您做一些好事:
- 将传入的值映射到另一个 observable
- 订阅它,所以它的值被发送到流中
- 自动管理从这些“内部订阅”中退订
在这种情况下,switchMap 是一个不错的选择,因为它一次只允许一个内部订阅,所以每当调用 myService.search(name) 时,都会创建一个新的 myService.create(name) 的内部订阅,而前一个是自动退订。
@Rafi Henig 的回答展示了一个很好的例子来说明这可能是什么样子。
- 注意
.pipe() 的使用。您可以使用管道运算符定义对可观察输出的转换,而无需实际订阅。
我建议你不要订阅 testMethod(),而是返回一个 observable。另外,让我们给“testMethod()”取一个更有意义的名字以供进一步讨论:“getPerson()”。
getPerson(name: string): Observable<Person> {
return this.myService.search(name).pipe(
switchMap(result => {
return iif(
() => result,
of(result),
this.myService.create(name).pipe(
map(({ id }) => ({ id, name }))
)
)
}),
tap(person => this.currentPerson = person)
);
}
console.log 显示未定义。有什么解决办法吗?
1 firstMethod() {
2 this.getPerson(name)
3 console.log(this.currentPerson)
4 }
undefined 的原因是因为代码是异步的。执行第 2 行,紧接着执行第 3 行,但异步工作尚未完成,因此尚未设置 this.currentPerson。
由于我们的 getPerson() 方法现在返回一个 observable,我们可以订阅并在订阅中执行您的 console.log():
1 firstMethod() {
2 this.getPerson(name).subscribe(
3 () => console.log(this.currentPerson)
4 )
5 }
为了简化,我们甚至不再需要this.currentPerson,因为这个人是通过流发出的!
1 firstMethod() {
2 this.getPerson(name).subscribe(
3 person => console.log(person)
4 )
5 }
既然你想...
学习如何以简洁的方式使用它编写代码
我认为最干净的方法可能是将您的“人员结果”定义为可观察的并抛弃this.currentPerson。
person$ = this.getPerson(name);
所以现在你有了this.person$,它可以被订阅,并且永远拥有 person 的当前值。无需“手动”更新this.currentPerson。
嗯……差不多。我们需要考虑当搜索词发生变化时会发生什么。
假设搜索词“名称”来自表单控件输入。
当使用Reactive Forms时,输入值是一个可观察的来源,所以我们可以从搜索词中定义我们的person$:
searchTerm$ = this.searchInput.valueChanges();
person$ = this.searchTerm$.pipe(
switchMap(searchTerm => this.getPerson(searchTerm))
);
getPerson(name: string): Observable<Person> {
return this.myService.search(name).pipe(
switchMap(result => {
return iif(
() => result,
of(result),
this.myService.create(name).pipe(
map(({ id }) => ({ id, name }))
)
)
})
);
}
请注意,我们已经定义了两个不同的 observable,但我们还没有订阅!现在,我们可以利用模板中的async 管道来处理订阅,让我们的组件代码保持简洁。
<p *ngIf="person$ | async as person">
We found {{ person.name }} !
</p>
我知道这有点冗长,但我希望您了解如何使用可管道运算符转换输出,以及如何定义一个可观察对象。