为什么用catchError处理错误而不是在Angular的订阅错误回调中处理

Mun*_*erz 5 rxjs angular2-observables angular

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

服务

getData(){
  return this.http.get('url')
}
Run Code Online (Sandbox Code Playgroud)

零件

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

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

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

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

fri*_*doo 10

1全部与Angular中关注点的分离有关

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

1.1让Components只关心数据的表示

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

组件不应直接获取或保存数据,它们当然也不应故意提供虚假数据。他们应该专注于呈现数据并将数据访问权委派给服务。
[Angular教程-为什么要服务]

假设您的数据是项目列表。您的Component会调用一个service.getItemList()函数,并且只关心数据,因此会期望:

  • 包含项目的列表
  • 空列表
  • 没有清单,即nullundefined

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

1.2不要让组件关心数据检索的细节,例如错误

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

从组件中删除数据访问意味着您可以随时更改实施思路,而无需接触任何组件。他们不知道该服务的工作方式。[Angular教程-获取英雄数据]

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

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

在数据检索服务中,您可以与catchError操作员一起详细处理错误。也许您想对所有错误执行某些操作,例如:

  • 记录下来
  • 将面向用户的错误消息显示为通知(请参阅显示消息
  • 获取替代数据或返回默认值

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

将错误报告给控制台后,处理程序将构造一条用户友好的消息,并向应用程序返回安全值,以便它可以继续工作。[Angular教程-HTTP错误处理]

1.4使未来的发展更加轻松

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

因此,归结为将数据检索逻辑(在“服务”中)与数据表示逻辑(在“组件”中)分开,以及将来扩展应用程序的简便性。

2保持可观察物存活

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

2.1链接多个可观察对象

看看这个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([]))
  );
}
Run Code Online (Sandbox Code Playgroud)

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

如果你想添加一个错误回调longLivedObservable$.subscribe(),并删除catchErrorgetHeroeslongLivedObservable$会抛出一个错误第一个HTTP请求之后,而不是终止,永不按钮点击反应再次之后。


测验:重要的是要添加到哪个“可观察” catchError

请注意,如果从内部Observable 移到外部Observable ,longLivedObservable$ 则终止catchErrorgetHeroes

// 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);
}
Run Code Online (Sandbox Code Playgroud)

“错误”和“完成”通知在可观察的执行期间只能发生一次,并且只能有一个。

在可观察的执行中,可能会传递零到无限的Next通知。如果传递了错误或完成通知,则此后将无法传递其他任何东西。
[RxJS文档-可观察]

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

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

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


2.2合并多个可观察对象

当您结合使用Observable时,例如使用forkJoincombineLatest,如果内部出现Observable错误,则可能希望外部Observable继续执行。

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)));
}
Run Code Online (Sandbox Code Playgroud)

animals$将发出一个包含可能获取的动物数组或null获取动物失败的数组。例如

[ [ Gorilla, Chimpanzee, Bonobo ], null, [ Asian Elefant, African Elefant ] ]
Run Code Online (Sandbox Code Playgroud)

这里catchError允许animals$Observable完成并发出一些东西。

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

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

  • 谢谢您,这实际上是很有意义的,我感谢您为回答整个过程付出的努力。 (3认同)
  • 我仍然不明白订阅是如何处理错误的 (3认同)