当前位置: 首页 > 工具软件 > rxjs-course > 使用案例 >

rxjs常见操作符_RxJS的5个常见错误

曾实
2023-12-01

rxjs常见操作符

重点 (Top highlight)

As a Consultant, I’ve had the privilege to work with many teams and projects in a relatively short amount of time.

作为顾问,我有幸在相对较短的时间内与许多团队和项目一起工作。

This not only allowed me to learn a lot from the existing codebases and team members but also to understand the biggest mistakes committed by the teams who weren’t very familiar with Rx and Angular.

这不仅使我可以从现有的代码库和团队成员中学到很多东西,而且还可以了解对Rx和Angular不太熟悉的团队所犯的最大错误。

1)不退订 (1) Not Unsubscribing)

I and many others have talked at length about the subject, but this is still the most common mistake I normally see — i.e. not unsubscribing from a stream at all.

我和许多人在对上述长度都谈到,但是这仍然是最常见的错误,我通常看到的-即不从流中的所有退订。

This has various consequences such as:

这会产生各种后果,例如:

  • Causing memory leaks

    导致内存泄漏
  • Causing unwanted callbacks being called

    导致不必要的回调被调用
  • Potentially, causing serious bugs in your app

    可能会导致您的应用程序出现严重错误

The best and most elegant way to unsubscribe from Observable is using a subject that emits a value when the component is destroyed.

取消订阅Observable的最好,最优雅的方法是使用在销毁组件时发出值的主题。

Alternatively, you can maintain a class property on your component with the subscription, and unsubscribe it when the component is destroyed.

另外,您可以通过订阅在组件上维护类属性,并在组件被销毁时取消订阅。

It’s pretty important, don’t forget.

这很重要,请不要忘记。

提示:与Bit共享和重用JS组件 (Tip: Share and Reuse JS Components with Bit)

Use Bit (Github) to share, document, and manage reusable components from different projects. It’s a great way to increase code reuse, speed up development, and build apps that scale.

使用Bit ( Github )共享,记录和管理来自不同项目的可重用组件。 这是增加代码重用,加速开发并构建可扩展应用程序的好方法。

Bit supports Angular, React, Vue, and more.

Bit支持Angular,React,Vue等。

2)省略初始值 (2) Omitting an initial value)

This happens pretty commonly with Angular forms. Say you subscribe to a stream expecting to get the value — but you never get one. How is that possible?

这在Angular表单中很常见。 假设您订阅了希望获得价值的信息流-但您从未获得价值。 那怎么可能?

Well — sometimes it’s caused by the fact that the stream was not initialized by an initial value, and an event was never dispatched.

好吧-有时是由于流没有由初始值初始化,并且从未调度事件而引起的。

Here’s a common scenario with Angular’s forms:

这是Angular表单的常见情况:

const name = new FormControl('Giancarlo');
const formGroup = new FormGroup({ name });
const valueChanges$ = formGroup.valueChanges;valueChanges.subscribe((value) => {
// do something
});

Unless the user changes the name FormControl in some way, the callback will never be called.

除非用户以某种方式更改name FormControl,否则将永远不会调用该回调。

But — we do want the subscription to emit using the initial value of the FormGroup (and many would expect it to do so).

但是-我们确实希望订阅使用FormGroup的初始值发出(很多人希望这样做)。

In this case, we need to push an initial value using the operator startWith:

在这种情况下,我们需要使用运算符startWith推送初始值:

const valueChanges$ = formGroup.valueChanges.pipe(
startWith(formGroup.value)
);valueChanges.subscribe((value) => {
// do something
});

In this case, we will receive an emission using the initial value of the form, and all the changes emitted after that.

在这种情况下,我们将使用表单的初始值接收到一个发射,并且此后发射所有更改。

3)使用错误的运算符 (3) Using the wrong Operators)

There are a lot of operators out there — and while you certainly don’t need to learn them all, you need to make sure you understand the details of each one that you are using.

那里有很多操作员-虽然您当然不需要全部学习它们,但是您需要确保您了解所使用的每个操作员的详细信息。

Small differences can have big consequences.

细微的差异可能会带来严重的后果。

示例:mergeMap vs switchMap (Example: mergeMap vs switchMap)

One of the most commonly used operators is mergeMap. This operator allows you to flatten an inner Observable and will maintain many active streams for each event: this is great in some situations, and not very ideal in others.

最常用的运算符之一是mergeMap 。 该运算符使您可以展平内部的Observable并为每个事件维护许多活动流:在某些情况下这很好,而在其他情况下则不是很理想。

In many cases, you may want to instead maintain only 1 active subscription. For example, if you have an event whose events call an HTTP endpoint, you may want to cancel the outgoing requests and only call the very latest one: in this case, you’ll be better off with switchMap.

在许多情况下,您可能只想维护1个活动订阅。 例如,如果您有一个事件,该事件的事件调用HTTP端点,则可能要取消传出的请求,而仅调用最新的请求:在这种情况下,最好使用switchMap

If you’re not careful, mergeMap may cause duplicate and unwanted subscriptions, while switchMap can lead to race conditions. Ultimately, both may lead to bugs and your code malfunctioning.

如果您不小心, mergeMap可能会导致重复的订阅和不需要的订阅,而switchMap可能会导致争用情况 。 最终,两者都可能导致错误和代码故障。

This is one example of the many, sometimes tiny, differences that make RxJS operators.

这是构成RxJS运算符的许多(有时很小)差异的一个示例。

As I said above — you do not need to know every Rx operator. You do need, though, to understand the small peculiarities of the ones you’re using and compare with similar others to understand which one is suitable in your case.

就像我在上面说的那样,您不需要了解每个Rx运算符。 但是,您确实需要了解所用产品的特殊性,并与其他同类产品进行比较,以了解哪种产品适合您的情况。

Other notable differences you should be aware of:

您应该注意的其他显着差异:

  • zip vs forkJoin vs combineLatest vs race

    邮编vs叉子加入vs组合最新vs种族
  • merge vs combineLatest

    合并与合并
  • timer vs interval

    计时器与间隔

  • never vs empty

    永不虚无
  • of vs from

    来自
  • buffer vs window

    缓冲区与窗口

4)使用错误的主题类型 (4) Using the wrong type of Subject)

Another important mistake not to commit is to not choose the wrong type of Subject for your task.

不要犯的另一个重要错误是不要为您的任务选择错误的主题类型。

Subjects are a special type of Observables that allow you to push values in the stream and also retrieve them by subscribing to it. While normal Subject likely cover most situations, there are slight differences that you should be aware of.

主题是Observable的一种特殊类型,它使您可以在流中推送值,也可以通过订阅它来检索它们。 虽然正常Subject可能涵盖大多数情况,但您应该注意一些细微差异。

后期订阅者 (Late Subscribers)

One common scenario is when your Subject emits events before an observer subscribes to its changes. If you’re expecting your subscriber to receive the data, you’re out of luck: it won’t.

一种常见的情况是您的主题在观察者订阅其更改之前发出事件。 如果您希望订户接收到数据,那么您就不走运了:它不会。

In this kind of scenario, you should instead opt for a ReplaySubject, which is able to replay all the events it received to also late subscribers. It is particularly useful also when you only want to keep the latest value in memory which you can do by defining its buffer size.

在这种情况下,您应该改为选择ReplaySubject ,它能够将收到的所有事件重播给后期订阅者。 当您只想将最新值保留在内存中时(通过定义其缓冲区大小可以执行此操作),它也特别有用。

Another alternative is the BehaviorSubject — which instead requires a value in order to be defined.

另一个选择是BehaviorSubject而是需要一个值才能定义。

5)在订阅回调中执行命令式逻辑 (5) Performing imperative logic inside the Subscription callback)

One of the greatest things about RxJs is that combining operators and reusing their logic is an incredibly nice (and easy) way to build reusable bits of code.

RxJ的最大优点之一是,组合运算符并重用其逻辑是一种构建可重用代码位的非常好(且简便)的方法。

Many of the benefits from writing Rx code ends once we subscribe to an Observable: the logic we write within subscription callbacks is not Rx-land and it’s the beginning of the end of FRP in our code.

一旦订阅了Observable,编写Rx代码的许多好处就结束了:在订阅回调中编写的逻辑不是Rx-land,而是代码中FRP结束的开始。

I am not saying you should never subscribe, of course, but my recommendation is to keep the logic within the subscription callbacks as small as possible — and wherever you can, avoid subscribing directly (for example, using the Angular async pipe).

我并不是说您永远不应该订阅,但是我的建议是使订阅回调中的逻辑尽可能小-并在任何可能的地方,避免直接订阅(例如,使用Angular async管道)。

What are the drawbacks of using logic within subscriptions?

在订阅中使用逻辑的缺点是什么?

有限的可重用性 (Limited Reusability)

RxJS streams are pipeable, which means they can be combined and extended and therefore reused.

RxJS流是可管道的,这意味着它们可以组合和扩展,因此可以重用。

Any time you subscribe and perform logic within the subscription, you take away some logic that instead could have been offloaded to an Rx operator:

每当您订阅并在订阅中执行逻辑时,您就会带走一些本可以卸载给Rx运算符的逻辑:

const allItems$ = this.service.items$.pipe(
filter(Boolean)
);const doneItems$ = allItems$.pipe(
map(items => items.filter(item => item.done)),
);const numberOfRemainingItems$ = combineLatest(
[allItems$, doneItems$]
).pipe(
map(([items, doneItems]) => items - doneItems),
);

As you can see, creating intermediate streams, or offloading logic to separate operators, is an awesome way of reusing logic across your application.

如您所见,创建中间流或将逻辑卸载到单独的运算符上,是在整个应用程序中重用逻辑的绝佳方法。

As a rule of thumb:

根据经验:

  • do not check whether a value is truthy within your subscription, you can easily handle it with the operator filter(Boolean)

    无需检查订阅中的值是否真实,您可以使用运算符filter(Boolean)轻松处理该filter(Boolean)

  • don’t transform data in your subscription

    不要转换您订阅中的数据
  • side effects: for example, showing/hiding a loading icon, can be done with the tap or/and the finalize operators

    副作用:例如,显示/隐藏加载图标,可以使用tap或/和finalize运算符来finalize

较少声明 (Less Declarative)

Let’s see an example between an imperative and a declarative snippet:

让我们看一下命令性和声明性代码段之间的示例:

class UsersDashboardComponent {
users: User[];
activeUsers: [];
bannedUsers: []; constructor(private service: UsersService) {
this.service.users$.subscribe(users => {
if (users) {
this.users = users;
this.activeUsers = users.filter(user => user.active);
this.bannedUsers = users.filter(user => user.banned);
}
});
}
}

Now, let’s convert the above declarative streams:

现在,让我们转换上面的声明性流:

class UsersDashboardComponent {
users$ = this.service.users$.pipe(
filter(Boolean)
); activeUsers$ = this.users$.pipe(
map(users => users.filter(user => user.active)),
); bannedUsers$ = this.users$.pipe(
map(users => users.filter(user => user.banned)),
); constructor(private service: UsersService) {}
}

With that said, don’t forget to subscribe either, otherwise, your observables will never emit.

话虽如此,也不要忘记订阅,否则,您的可观察对象将永远不会发出。

最后的话 (Final Words)

Rx is a pretty awesome library and a tool that can help you handle complex asynchronous aspects of your application with ease. It’s also quite big, and most often misunderstood.

Rx是一个非常出色的库和工具,可以帮助您轻松处理应用程序的复杂异步方面。 它也很大,并且经常被误解。

Making sure you follow the recommendations above will at least ensure that you’re taking care of an extremely common cause of mistakes, or bugs that you spend hours trying to fix.

确保遵循上述建议,至少可以确保您照顾到非常常见的错误或花费数小时试图修复的错误的原因。

In summary

综上所述

  • Unsubscribe, always

    总是退订
  • Don’t omit an initial value if you expect one

    如果期望一个初始值,请不要忽略
  • Learn well the operators that you’re using. Small differences can lead to big mistakes.

    充分了解您正在使用的操作员。 细微的差异会导致重大错误。
  • Use the right type of Subject: they are suited for different use-cases

    使用正确的主题类型:它们适用于不同的用例
  • Write as much logic as possible declaratively and within your streams. Also, reuse the logic with custom operators or intermediate streams.

    在流中尽可能声明性地编写逻辑。 另外,将逻辑与自定义运算符或中间流一起重用。

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment!

如果您需要任何澄清,或者您认为不清楚或错误的地方,请发表评论!

学到更多 (Learn More)

I hope you enjoyed this article! If you did, follow me on Medium, Twitter or my website for more articles about Software Development, Front End, RxJS, Typescript, and more! Also, I started a new blog called Angular Bites where I post blog posts about Angular. Come take a look!

希望您喜欢这篇文章! 如果您愿意,请在 MediumTwitter 或我的 网站 上关注我以获取有关软件开发,前端,RxJS,Typescript等的更多文章! 另外,我创建了一个名为 Angular Bites 的新博客 ,并在其中发布了有关Angular的博客文章。 来看看!

翻译自: https://blog.bitsrc.io/5-common-mistakes-with-rxjs-1b09d4c19387

rxjs常见操作符

 类似资料: