javascript - 如何以更简洁的方式编写嵌套订阅?

标签 javascript angular typescript rxjs

我是 RxJS 的新手,我想学习如何以简洁的方式使用它编写代码。我有嵌套订阅,我试图重写它,但没有结果。

firstMethod() {
  this.testMethod(name)
  console.log(this.currentPerson)
}

testMethod() {
  this.myService.search(name).subscribe(response => {
    if(result) {
      this.currentPerson = result
    } else {
        this.myService.create(name).subscribe(result => {
          const person = {id: result.id, name: name}
          this.currentPerson = person
        })
      }
   })
}

不幸的是,尽管代码很乱,'else' 之后的某些部分还是有问题,因为 console.log 显示未定义。 有什么解决方法吗?

最佳答案

要有效地处理嵌套订阅,您应该使用“Higher Order Mapping Operator”之一,它可以为您做一些好事:

  1. 将传入值映射到另一个可观察值
  2. 订阅它,所以它的值被发送到流中
  3. 管理自动取消订阅这些“内部订阅”

在这种情况下,switchMap 是一个不错的选择,因为它一次只允许一个内部订阅,所以每当 myService.search(name) 被调用时myService.create(name) 的新内部订阅被创建,之前的订阅自动取消订阅。

@Rafi Henig 的回答展示了一个很好的例子。

  • 注意 .pipe() 的使用。您可以使用管道运算符定义对可观察输出的转换,而无需实际订阅。

我建议您不要在 testMethod() 中订阅,而是返回一个可观察对象。此外,让我们为“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 shows undefined. Any tips how to fix it?

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  }

既然你想...

learn how to write code using it in clean way

我认为最干净的方法可能是将您的“人员结果”定义为可观察的并放弃 this.currentPerson

person$ = this.getPerson(name);

现在你有了 this.person$,它可以被订阅并且总是有 person 的当前值。无需“手动”更新 this.currentPerson

嗯……差不多吧。我们需要考虑当搜索词发生变化时会发生什么。

假设搜索词“name”来自表单控件输入。

使用 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 }))
        )
      )
    })
  );
}

注意我们已经定义了两个不同的可观察对象,但是我们还没有订阅!现在,我们可以利用 async在我们的模板中使用管道来处理订阅,使我们的组件代码简洁明了。

<p *ngIf="person$ | async as person">
  We found {{ person.name }} !
</p>

我知道这有点啰嗦,但我希望您了解如何使用可管道运算符转换输出以及如何定义一个可观察对象。

关于javascript - 如何以更简洁的方式编写嵌套订阅?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63711336/

相关文章:

javascript - React Router 嵌套路由不渲染

angular - 如何使用 angular2 设置验证器不接受索引 0 的选定选项

javascript - div 内容显示在文本框 HTML Javascript 上

php - 比较两个具有相同结构的 XML 文件并找出差异

Javascript:获取元素的当前 "onclick"内容

javascript - 如何跳到下一个描述 Mocha 错误?

angular - 如何防止 Angular 中用户角色的操作

javascript - 创建的 Angular 组件在 'ng build' 之后不可见

javascript - Angular 13 - BrowserAuthError : interaction_in_progress: Interaction is currently in progress

javascript - Angular 4 - 如何根据数量和选项的变化计算价格