Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后

Posted

技术标签:

【中文标题】Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后【英文标题】:Angular 6 / Rxjs - how to basics: observables success, error, finally 【发布时间】:2018-12-07 14:21:57 【问题描述】:

我正在最新的 Angular 6 上构建一个架构,并且来自 AngularJS,有一些我无法解决的问题:HTTP 请求的基本处理。

所以,为了这个问题,假设我想要一个 observable。因为它似乎是 Angular 的未来。

我在 AngularJS 中使用了非常优雅的东西:

   service.getAll()
    .then(onSuccess) // I process the data
    .catch(onError) // I do whatever needed to notify anyone about the issue
    .finally(onFinally); // I stop the loading spinner and other stuff

现在在 Angular 6/RxJS 6 中,我不明白为什么一切都如此复杂而且看起来不正确。

我可以找到两种方法来做与上面相同的事情:

    完整的管道

    this.service.getAll()
        .pipe(
            map((data) => this.onSuccess(data)),
            catchError(error => of(this.handleError(error))),
            finalize(() => this.stopLoading())
        )
        .subscribe();
    

由于我们必须使用管道进行最终确定,我不妨对所有内容都使用管道,我认为最好让所有内容都按相同的顺序排列。但是现在我们必须抛出一些东西,叫做“of”(不太容易理解),我不喜欢这样。

    半管 所以我尝试了另一个想法,只使用我需要的管道(最终确定)并保留订阅回调。

    this.service.getAll()
    .pipe(
        finalize(() => this.stopLoading())
    )
    .subscribe(
        (data) => this.onSuccess(data),
        (error) => this.handleError(error)
    );
    

但是,好吧。是不是有点落后了?我们仍然有没有实际名称的回调,我们在读取处理和错误之前完成。很奇怪。

所以有些东西我绝对不明白。而且我在网上找不到与这个基本问题相关的任何内容。您要么有人想要“成功并最终”,要么有人想要“成功和错误”,但没有人想要其中的 3 个。 也许我太老了,我不了解新的最佳实践(如果是这样,请教育我!)。

我的需求很简单: 1. 我想处理从服务获得的数据 2. 我想得到错误以便显示给用户 3.我想在调用之前停止我刚刚启动的加载微调器,或者在第一个完全成功或错误后再次调用(我真的想要一个finally)

您如何使用 observable 处理基本的 HTTP 调用?

(我不想要任何.toPromise,拜托,我想了解如何处理新东西)

【问题讨论】:

【参考方案1】:

我认为有一个关键的误解:

你要么有人想要“成功和最终”,或者“成功和错误”,但没有人想要他们中的 3 个。

这并不完全正确。每个 Observable 可以发送零个或多个 next 通知和一个 errorcomplete 通知,但不能同时发送两者。例如,当进行成功的 HTTP 调用时,您将收到一个 next 和一个 complete 通知。在错误的 HTTP 请求中,您将只有一个 error 通知,仅此而已。见http://reactivex.io/documentation/contract.html

这意味着你永远不会有一个 Observable 同时发射 errorcomplete

然后是finalize 运算符。处理链时调用此运算符(也包括普通取消订阅)。换句话说,它在errorcomplete 通知之后被称为

所以你的第二个例子是正确的。我知道您在订阅之前包含finalize 看起来很奇怪,但实际上,源 Observable 的每个发射首先从上到下到达订阅者,如果它的errorcomplete 通知它会触发处置处理程序自下而上(以相反的顺序),此时调用finalize。见https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subscriber.ts#L150-L152

在您的示例中,使用 finalize 与将 dispose 处理程序自己添加到 Subscription 对象中相同。

const subscription = this.service.getAll()
  .subscribe(
    (data) => this.onSuccess(data),
    (error) => this.handleError(error)
  );

subscription.add(() => this.stopLoading());

【讨论】:

这非常接近我的想法。谢谢你的解释。我只有“添加”这个词而不是“最后”,但由于它的阅读顺序正确,我认为这没关系。 this.service.getAll().subscribe(success,error).add(finally); @SimonPeyou 我和你在同一个地方,由于这种奇怪的命名约定,从 Promises 跳转到 Observables 是如此令人难以置信的粗糙。在 HTTP 请求上,我更喜欢使用拦截器来捕获错误并让 promises/observables 失败并最终清理。 很好的解释,虽然我花了一些时间才找到 add() 的描述,Adds a tear down to be called during the unsubscribe() of this Subscription【参考方案2】:

我认为正确的方法是使用 Observable 函数:next、err、complete。 这是一个如何触发完整功能的示例。 假设我们有 BehaviorSubject 对象:

let arr = new BehaviorSubject<any>([1,2]);

现在假设我们想要订阅它,如果我们得到值“完成”我们想要完成。

let arrSubscription = arr.asObservable().subscribe(
  data => 
      console.log(data)
      if(data === 'finish') 
        arr.complete()
      
  ,
  err => 
      console.log(err)
  ,
  () => 
      console.log("Complete function triggered.")
  
);
arr.next([3,4])
arr.next('finish')
arr.next([5,6])

控制台日志是:

[1,2]
[3,4]
finish
Complete function triggered.

由于我们触发了 complete 函数,因此 BehaviorSubject 的最后一个 .next 不会被触发,因为 err 和 complete 函数是订阅的终止符。 这只是一个示例,您可以如何触发完整的功能,从这里您可以做任何您想做的事情。

【讨论】:

很抱歉,我看不出这是如何与 Angular HTTP observable 一起工作的。如果我错了,我也需要调用完成。我不妨在那里调用我的 postCompletion 函数。 不,error 和 complete 是订阅的终止符,而数据是无限的。如果你有错误,你应该处理错误,它与处理完整的行为不同。 在我的示例中,我应该在哪里调用 stopLoading? 如果 stopLoading 是一个在错误和完整时都表现相同的函数,而不是你必须调用两者......错误你可能想做不同的事情,比如说向用户显示一条消息加上错误图标,并且您只想停止微调器。这一切都取决于您的需求。 那么,finalize 没有任何意义。在这种情况下,我的意思是。【参考方案3】:

Observable 的 subscribe 方法接受 3 个可选函数作为参数

第一个处理由引发的事件附带的数据 可观察的 第二个处理任何错误(如果发生) 第三个在 Observable 完成时做某事

所以,如果我理解正确的话,你想要的可以用这样的代码来实现

this.service.getAll()
.subscribe(
    data => this.onSuccess(data),
    error => this.handleError(error),
    () => this.onComplete()
);

考虑一下,当您想要重试(请参阅retry 运算符)以防出现竞争条件(通过使用switchMap 运算符)时,使用 Observables 进行 http 调用可以带来好处。我认为这些是 Angular 团队为 http 客户端选择这种方法的主要原因。

一般来说,我认为值得开始了解 Observables 是如何工作的以及一些最重要的运算符(除了上面的那些,我首先想到的是 mergeMapfilterreduce - 然后还有还有许多其他)很重要,因为它们可以显着简化异步非阻塞环境中的许多任务,例如浏览器(或节点)。

【讨论】:

遗憾的是,如果我错了,请纠正我,当一切顺利时,.subscribe 签名中的“完整”参数被触发。如果发生错误,它不会被触发。我理解 Observable 的好处,我并不是反对它们。我只是想确定如何做这个基本的(也许为什么它不再是基本的好习惯了)。 是的,你是对的,completeerror 或者什么都没有,对于永远不会完成的 Observables。我不明白悲伤的部分。 与上面的评论相同,我开始认为我错过了一些明显的东西:在我的示例中,我应该在哪里调用 stopLoading? @Picci 我认为他想要的最终没有完成,只有在成功后才会调用。他想要的就像是成功或失败后的封闭。检查此***.com/a/44771000/667767 以查看完成和最终完成之间的区别

以上是关于Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后的主要内容,如果未能解决你的问题,请参考以下文章

Angular/RxJS 6:如何防止重复的 HTTP 请求?

rxjs-websockets - 如何封装为 Angular 6/7 服务

Angular 6 RXJS 导入语法?

如何使用 RxJS 在 Angular 6 中发出一系列 http 请求

Angular 6 和 RxJS 6 重大变化

更新到 Angular 6 和 rxjs 6 后 Plunker 坏了