为什么用catchError处理错误而不是在Angular的订阅错误回调中处理 [英] Why handle errors with catchError and not in the subscribe error callback in Angular

查看:54
本文介绍了为什么用catchError处理错误而不是在Angular的订阅错误回调中处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我通常会这样写我的http请求

So I'd normally write my http requests like so

服务

getData(){
  return this.http.get('url')
}

组件

getTheData() {
  this.service.getData().subscribe(
    (res) => {
      //Do something
    }, 
    (err) => {
      console.log('getData has thrown and error of', err)
    })

但是在浏览Angular文档时,他们似乎在服务中将其格式化为

But looking through Angular documentation they seem to format it like so in a Service

getHeroes(): Observable<Hero[]> {
  return this.http.get<Hero[]>(this.heroesUrl)
    .pipe(
      catchError(this.handleError('getHeroes', []))
    );
}

这个隐含的上行空间是什么,因为它对我来说似乎很冗长,而且我个人从来就不需要传达我的错误.

What's the implicit upside of this as it seems quite verbose to me and I've personally never had the need to pipe my errors.

推荐答案

1都是关于Angular中关注点分离的问题

使用catchError的一个主要好处是 将整个数据检索逻辑与包括在显示过程中可能发生的所有错误分离开来.

1 It's all about separation of concern in Angular

One major benefit of using catchError is to separate the whole data retrieval logic including all errors that can occur along the way from the presentation of the data.

组件仅应关心数据(无论是否存在).他们不应该关心如何检索数据的细节,也不必关心数据检索过程中可能出错的所有事情.

Components should only care about data (whether it's there or not). They shouldn't care about the specifics of how to retrieve data or all the things that could go wrong during data retrieval.

组件不应该直接获取或保存数据,它们肯定是 不应故意提供虚假数据.他们应该专注于呈现 数据并委托对服务的数据访问.
[Angular Tutorial-Why Services]

Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service.
[Angular Tutorial - Why Services]

比方说,您的数据是一个项目列表.您的组件将调用service.getItemList()函数,并且由于它只关心数据,因此会期望:

Let's say your data is a list of items. Your Component would call a service.getItemList() function and, as it only cares about data, would expect:

  • 包含项目的列表
  • 一个空列表
  • 没有列表,即nullundefined
  • a list containing items
  • an empty list
  • no list i.e. null or undefined

您可以使用Component模板中的ngIf轻松处理所有这些情况,并根据情况显示数据或其他内容.使Service函数返回一个 clean 可观察的对象,该对象仅返回数据(或null)并且不会引发任何错误,这使您组件中的代码保持精简,因为您可以轻松地使用 AsyncPipe 进行订阅.

You could easily handle all these cases with ngIf in your Component template and display the data or something else depending on the case. Having a Service function return a clean Observable that only returns data (or null) and isn't expected to throw any errors keeps the code in your Components lean as you can easily use the AsyncPipe in a template to subscribe.

您的数据检索和错误处理逻辑可能会随着时间而变化.也许您要升级到新的Api并突然不得不处理不同的错误.不要让您的组件为此担心.将此逻辑移至服务.

Your data retrieval and error handling logic may change over time. Maybe you're upgrading to a new Api and suddenly have to handle different errors. Don't let your Components worry about that. Move this logic to a Service.

从组件中删除数据访问权限意味着您可以改变主意. 随时实施,而无需涉及任何组件.他们不 知道服务的运作方式. [Angular教程-获取英雄数据]

Removing data access from components means you can change your mind about the implementation anytime, without touching any components. They don't know how the service works. [Angular Tutorial - Get hero data]

1.3将数据检索和错误处理逻辑放在服务中

处理错误是数据检索逻辑的一部分,而不是数据表示逻辑的一部分.

1.3 Put the data retrieval and error handling logic in a Service

Handling errors is part of your data retrieval logic and not part of your data presentation logic.

在数据检索服务中,您可以使用catchError运算符详细处理错误.也许您想针对所有错误执行某些操作,例如:

In your data retrieval Service you can handle the error in detail with the catchError operator. Maybe there are some things you want to do on all errors like:

  • 记录
  • 将面向用户的错误消息显示为通知(请参见显示消息)
  • 获取备用数据或返回默认值
  • log it
  • display a user oriented error message as a notification (see Show messages)
  • fetch alternative data or return a default value

将其中的一些内容移到this.handleError('getHeroes', [])函数中可以避免重复代码.

Moving some of this into a this.handleError('getHeroes', []) function keeps you from having duplicate code.

将错误报告给控制台后,处理程序将构造一个用户 友好的消息,并向应用返回安全值,以便它可以保持 在职的. [Angular教程-HTTP错误处理]

After reporting the error to console, the handler constructs a user friendly message and returns a safe value to the app so it can keep working. [Angular Tutorial - HTTP Error handling]

1.4使将来的开发更容易

有时可能需要从新的组件调用现有的Service函数.将错误处理逻辑包含在Service函数中将使此操作变得容易,因为您不必担心从新Component调用函数时的错误处理.

1.4 Make future development easier

There may come a time when you need to call an existing Service function from a new Component. Having your error handling logic in a Service function makes this easy as you won't have to worry about error handling when calling the function from your new Component.

因此归结为将数据检索逻辑(在Services中)与数据表示逻辑(在Components中)和将来扩展应用程序的简便性分开.

So it comes down to separating your data retrieval logic (in Services) from your data presentation logic (in Components) and the ease of extending your app in the future.

catchError的另一个用例是在构造更复杂的链式或组合式Observable时保持Observable处于活动状态.在内部Observable上使用catchError可使您从错误中恢复并使外部Observable保持运行.当您使用订阅错误处理程序时,这是不可能的.

Another use case of catchError is to keep Observables alive when you're constructing more complex chained or combined Observables. Using catchError on inner Observables allows you to recover from errors and keep the outer Observable running. This isn't possible when you're using the subscribe error handler.

看看这个longLivedObservable$:

// will never terminate / error
const longLivedObservable$ = fromEvent(button, 'click').pipe(
  switchMap(event => this.getHeroes())
);
longLivedObservable$.subscribe(console.log);

getHeroes(): Observable<Hero[]> {
  return this.http.get<Hero[]>(this.heroesUrl).pipe(
    catchError(error => of([]))
  );
}

每单击一个按钮,longLivedObservable$就会执行一个http请求.即使内部http请求抛出错误,它也永远不会终止,因为在这种情况下,catchError返回的Observable不会出错,而是发出一个空数组.

The longLivedObservable$ will execute a http request whenever a button is clicked. It will never terminate not even when the inner http request throws an error as in this case catchError returns an Observable that doesn't error but emits an empty array instead.

如果您将错误回调添加到longLivedObservable$.subscribe()并在getHeroes中删除了catchError,则longLivedObservable$会在第一个发出错误的http请求后终止,并且此后再也不会对按钮单击做出反应.

If you would add an error callback to longLivedObservable$.subscribe() and removed catchError in getHeroes the longLivedObservable$ would instead terminate after the first http request that throws an error and never react to button clicks again afterwards.

惊喜:将catchError

Excursus: It matters to which Observable you add catchError

请注意,如果将catchErrorgetHeroes的内部Observable移至外部Observable,则longLivedObservable$ 将终止.

Note that longLivedObservable$ will terminate if you move catchError from the inner Observable in getHeroes to the outer Observable.

// will terminate when getHeroes errors
const longLivedObservable = fromEvent(button, 'click').pipe(
  switchMap(event => this.getHeroes()),
  catchError(error => of([]))
);
longLivedObservable.subscribe(console.log); 

getHeroes(): Observable<Hero[]> {
  return this.http.get<Hero[]>(this.heroesUrl);
}

错误"和完成"通知只能在 可观察的执行,并且只能是其中之一.

"Error" and "Complete" notifications may happen only once during the Observable Execution, and there can only be either one of them.

在可观察的执行中,零到无限的下一个通知可能是 发表.如果传递了错误或完成通知, 那么之后就什么也不能传递了.
[RxJS文档-可观察]

In an Observable Execution, zero to infinite Next notifications may be delivered. If either an Error or Complete notification is delivered, then nothing else can be delivered afterwards.
[RxJS Documentation - Observable]

当传递错误(或完成)通知时,可观察项终止.之后他们什么也不能发射.在Observable上使用catchError不会更改此设置. catchError不允许您的源Observable在发生错误后继续发射,它仅允许您在发生错误时切换到其他Observable.此切换仅发生一次,因为只能传递一个错误通知.

Observables terminate when an Error (or Complete) notification is delivered. They can't emit anything else afterwards. Using catchError on an Observable doesn't change this. catchError doesn't allow your source Observable to keep emitting after an error occurred, it just allows you to switch to a different Observable when an error occurs. This switch only happens once as only one error notification can be delivered.

在上面的示例中,当this.getHeroes()错误发生时,此错误通知将传播到外部流,导致从fromEvent(button, 'click')取消订阅,并且catchError切换到of([]).

In the example above, when this.getHeroes() errors this error notification is propagated to the outer stream leading to an unsubscribe from fromEvent(button, 'click') and catchError switching to of([]).

catchError不会将错误通知暴露给外部流.因此,如果要使外部Observable保持活动状态,则必须使用内部Observable上的catchError处理错误,即直接在发生错误的地方进行处理.

Placing catchError on the inner Observable doesn't expose the error notification to the outer stream. So if you want to keep the outer Observable alive you have to handle errors with catchError on the inner Observable, i.e. directly where they occur.

当您合并Observables时,例如如果出现内部可观察错误,则使用forkJoincombineLatest您可能希望外部可观察对象继续运行.

When you're combining Observables e.g. using forkJoin or combineLatest you might want the outer Observable to continue if any inner Observable errors.

const animals$ = forkJoin(
  this.getMonkeys(), 
  this.getGiraffes(), 
  this.getElefants()
);
animals$.subscribe(console.log);

getMonkeys(): Observable<Monkey[]> {
  return this.http.get<Monkey[]>(this.monkeyUrl).pipe(catchError(error => of(null)));
}

getGiraffes(): Observable<Giraffe[]> {
  return this.http.get<Giraffe[]>(this.giraffeUrl).pipe(catchError(error => of(null)));
}

getElefants(): Observable<Elefant[]> {
  return this.http.get<Elefant[]>(this.elefantUrl).pipe(catchError(error => of(null)));
}

animals$将发出一个包含可能要提取的动物数组的数组,或者null会在提取动物失败的地方发出一个数组.例如

animals$ will emit an array containing the animal arrays it could fetch or null where fetching animals failed. e.g.

[ [ Gorilla, Chimpanzee, Bonobo ], null, [ Asian Elefant, African Elefant ] ]

在这里catchError允许animals$ Observable完成并发出某些东西.

Here catchError allows the animals$ Observable to complete and emit something.

如果要从所有获取函数中删除catchError,而是向animals$.subscribe()添加错误回调,则如果任何内部Observable出错,则animals$将会出错,因此即使某些内部Observable成功完成也不会发出任何信息.

If you would remove catchError from all fetch functions and instead added an error callback to animals$.subscribe() then animals$ would error if any of the inner Observables errors and thus not emit anything even if some inner Observables completed successfully.

要了解更多信息,请阅读: RxJs错误处理:完整的实用指南

To learn more read: RxJs Error Handling: Complete Practical Guide

这篇关于为什么用catchError处理错误而不是在Angular的订阅错误回调中处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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