【问题标题】:How to throw error from RxJS map operator (angular)如何从 RxJS 映射运算符中抛出错误(角度)
【发布时间】:2017-08-29 04:56:22
【问题描述】:

我想根据条件从我的 observable 的 ma​​p 运算符中抛出一个错误。例如,如果没有收到正确的 API 数据。请看以下代码:

private userAuthenticate( email: string, password: string ) {
    return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
        .map( res => { 
            if ( res.bearerToken ) {
                return this.saveJwt(res.bearerToken); 
            } else {
                // THIS DOESN'T THROW ERROR --------------------
                return Observable.throw('Valid token not returned');
            }
        })
        .catch( err => Observable.throw(this.logError(err) )
        .finally( () => console.log("Authentication done.") );
}

基本上你可以在代码中看到,如果响应(res 对象)没有bearerToken,我想抛出一个错误。因此,在我的订阅中,它会进入下面提到的第二个参数 (handleError)。

.subscribe(success, handleError)

有什么建议吗?

【问题讨论】:

  • throw 'Valid token not returned'; 呢?
  • 编译失败
  • 请提供确切的错误信息。
  • 哦,抱歉,它不适用于return throw 'message here',但可以在没有return 关键字的情况下使用。让我检查一下它的逻辑是否正确。
  • subscribe 方法中没有收到错误文本,流中的.finally() 也会触发。 (但是执行停止,这是一件好事)

标签: angular typescript rxjs


【解决方案1】:

只需在map() 运算符中抛出错误。 RxJS 中的所有回调都用 try-catch 块包装,因此它会被捕获,然后作为 error 通知发送。

这意味着你不返回任何东西,只是抛出错误:

map(res => { 
  if (res.bearerToken) {
    return this.saveJwt(res.bearerToken); 
  } else {
    throw new Error('Valid token not returned');
  }
})

throwError()(前 RxJS 5 中的 Observable.throw())是一个 Observable,它只发送一个 error 通知,但 map() 并不关心你返回什么。即使您从 map() 返回 Observable,它也会作为 next 通知传递。

最后一件事,你可能不需要使用 .catchError()(在 RxJS 5 中以前是 catch())。如果您需要在发生错误时执行任何副作用,最好使用 tap(null, err => console.log(err))(RxJS 5 中的前 do())。

2019 年 1 月:针对 RxJS 6 更新

【讨论】:

  • 谢谢@martin - 是的,您的解决方案有效。我实际上在我的 logError 方法中也遇到了一个问题,@GünterZöchbauer 指出了这一点。我不得不 return 来自它的错误对象,现在它完美地工作了:) 谢谢!
  • @martin:你能解释一下为什么我们不希望你在这里 .catch() 吗?
  • @Bob 因为 OP 仅使用 catch() 来记录并重新抛出错误,如果您只想执行副作用(记录错误),这是不必要的,而且只使用 @ 更容易987654336@
  • 这是否与return throwError(new Error('Valid token not returned')); 相同?
  • @Simon_Weaver 不,不是。 return throwError() 返回一个Observable<never>,这只是立即中断可观察流,根本不返回。
【解决方案2】:

如果你觉得 throw new Error() 看起来像 un-observable-like 你可以使用 return throwError(...)switchMap 而不是 map(区别在于 switchMap 必须返回一个新的可观察而不是原始值):

// this is the import needed for throwError()
import { throwError } from 'rxjs';


// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => { 
   if (res.bearerToken) {
      return of(this.saveJwt(res.bearerToken)); 
   } 
   else {
      return throwError('Valid token not returned');  // this is 
   }
});

或更简洁:

this.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

行为相同,只是语法不同。

您实际上是在说从管道中的 HTTP 可观察对象“切换”到不同的可观察对象,这要么只是“包装”输出值,要么是新的“错误”可观察对象。

不要忘记输入of,否则您会收到一些令人困惑的错误消息。

'switchMap' 的美妙之处还在于,如果您愿意,您可以返回一个全新的命令“链”——无论需要使用saveJwt 完成什么逻辑。

【讨论】:

  • 一旦我开始将 switchMap 视为异步 if 语句 - 事情就更有意义了 :-)
  • 如果遇到Importing this module is blacklisted. Try importing a submodule instead. (import-blacklist)tslint(1),请改用import { throwError } from 'rxjs/internal/observable/throwError';
  • 提醒,从 RxJS 8 开始,throwError(...) 参数是原始参数(例如错误消息)将被弃用。相反,传递一个错误工厂函数,即throwError(() => new Error(...))
【解决方案3】:

即使这个问题已经得到解答,我还是想分享一下我自己的方法(尽管它与上面的方法略有不同)。

我将决定与映射分开返回的内容,反之亦然。我不确定哪个运算符最适合这个,所以我会使用tap

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);

【讨论】:

  • tap 的返回值被忽略。这段代码做的和它说的不一样
  • 我还在习惯 rxjs。使用 switchMap 会更好吗?有人可以建议不同的运营商或直接编辑吗?
  • 我认为建议的throw new Error() 是迄今为止最好的选择
  • 我最初对这种方法持怀疑态度,但 rxjs 文档有类似的示例 rxjs.dev/api/operators/tap#example-2
【解决方案4】:

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2021-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多