如何以更简洁的方式编写嵌套订阅? [英] How to write nested subscribe in cleaner way?

查看:43
本文介绍了如何以更简洁的方式编写嵌套订阅?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

firstMethod() {this.testMethod(name)控制台日志(this.currentPerson)}测试方法() {this.myService.search(name).subscribe(response => {如果(结果){this.currentPerson = 结果} 别的 {this.myService.create(name).subscribe(result => {const person = {id: result.id, name: name}this.currentPerson = 人})}})}

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

解决方案

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

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

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

@Rafi Henig 的回答展示了一个很好的例子,说明了这可能是什么样子.

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

我建议您不要在 testMethod() 中订阅,而是返回一个 observable.另外,让我们给testMethod()"一个更有意义的名称供进一步讨论:getPerson()".

getPerson(name: string): Observable{返回 this.myService.search(name).pipe(开关映射(结果 => {返回 iif(() =>结果,的(结果),this.myService.create(name).pipe(地图(({ id }) => ({ id, name }))))}),点击(人 => this.currentPerson = 人));}

<块引用>

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 () =>控制台日志(this.currentPerson)4)5 }

为了简化,我们甚至不再需要 this.currentPerson,因为这个人是通过流发出的!

1 firstMethod() {2 this.getPerson(name).subscribe(3人=>控制台日志(人)4)5 }

既然你想...

<块引用>

学习如何以干净的方式使用它编写代码

我认为最简洁的方法可能是定义您的人物结果";作为 observable 并抛弃 this.currentPerson.

person$ = this.getPerson(name);

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

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

让我们假设搜索词名称"是来自表单控件输入.

当使用 Reactive Forms 时,输入值是一个可观察的来源,因此我们可以定义我们的person$ 来自搜索词:

searchTerm$ = this.searchInput.valueChanges();人$ = this.searchTerm$.pipe(switchMap(searchTerm => this.getPerson(searchTerm)));getPerson(name: string): Observable{返回 this.myService.search(name).pipe(开关映射(结果 => {返回 iif(() =>结果,的(结果),this.myService.create(name).pipe(地图(({ id }) => ({ id, name }))))}));}

请注意,我们定义了两个不同的 observable,但我们还没有订阅!现在,我们可以利用模板中的 async 管道来处理订阅,使我们的组件代码保持简洁.

我知道这有点冗长,但我希望您了解如何使用可管道操作符转换输出,以及如何从另一个定义一个可观察对象.

I am new with RxJS and I want to learn how to write code using it in clean way. I have nested subscription and I' ve tried to rewrite this, but with no result.

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
        })
      }
   })
}

Unfortunatelly despite the code is messy, there is something wrong with some piece after 'else' because console.log shows undefined. Any tips how to fix it?

解决方案

To effectively handle nested subscribes, you should use one of the "Higher Order Mapping Operator", which do a few nice things for you:

  1. maps the incoming value to another observable
  2. subscribes to it, so its values are emitted into the stream
  3. manages unsubscribing from these "inner subscriptions" automatically

In this case, switchMap is a good choice because it will only allow a single inner subscription at a time, so whenever myService.search(name) is called a new inner subscription for myService.create(name) is created, and the previous one is automatically unsubscribed.

@Rafi Henig's answer shows a good example of what this could look like.

  • Notice the use of .pipe(). You can define transformations to your observable output using pipeable operators without actually subscribing.

I would suggest you don't subscribe in your testMethod(), but rather return an observable. Also, let's give "testMethod()" a more meaningful name for further discussion: "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  }

The reason for the undefined is because the code is asynchronous. Line 2 is executed, then line 3 immediately after, but the async work hasn't been finished yet, so this.currentPerson hasn't been set yet.

since our getPerson() method now returns an observable, we can subscribe and do your console.log() inside the subscribe:

1  firstMethod() {
2    this.getPerson(name).subscribe(
3       () => console.log(this.currentPerson)
4    )
5  }

To simplify, we don't even need this.currentPerson anymore, because the person is emitted through the stream!

1  firstMethod() {
2    this.getPerson(name).subscribe(
3       person => console.log(person)
4    )
5  }

And since you want to...

learn how to write code using it in clean way

I think think cleanest way would probably be to define your "person result" as an observable and ditch this.currentPerson.

person$ = this.getPerson(name);

So now you have this.person$ which can be subscribed to and will always have the current value of person. No need to "manually" update this.currentPerson.

Well... almost. We need to consider what happens when the search term changes.

Let's assume the search term "name" is coming from a form control input.

When using Reactive Forms the input value is an observable source, so we can define our person$ from the search term:

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

Notice we've defined two different observables, but we haven't subscribed yet! Now, we can leverage the async pipe in our template to handle the subscription, keeping our component code nice and simple.

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

I know this has gotten a bit long winded, but I hope you see how its possible to transform output using pipeable operators and how you can define one observable from another.

这篇关于如何以更简洁的方式编写嵌套订阅?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆