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
通知和一个 error
或 complete
通知,但不能同时发送两者。例如,当进行成功的 HTTP 调用时,您将收到一个 next
和一个 complete
通知。在错误的 HTTP 请求中,您将只有一个 error
通知,仅此而已。见http://reactivex.io/documentation/contract.html
这意味着你永远不会有一个 Observable 同时发射 error
和 complete
。
然后是finalize
运算符。处理链时调用此运算符(也包括普通取消订阅)。换句话说,它在error
和complete
通知之后被称为。
所以你的第二个例子是正确的。我知道您在订阅之前包含finalize
看起来很奇怪,但实际上,源 Observable 的每个发射首先从上到下到达订阅者,如果它的error
或complete
通知它会触发处置处理程序自下而上(以相反的顺序),此时调用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 个可选函数作为参数
所以,如果我理解正确的话,你想要的可以用这样的代码来实现
this.service.getAll()
.subscribe(
data => this.onSuccess(data),
error => this.handleError(error),
() => this.onComplete()
);
考虑一下,当您想要重试(请参阅retry
运算符)以防出现竞争条件(通过使用switchMap
运算符)时,使用 Observables 进行 http 调用可以带来好处。我认为这些是 Angular 团队为 http 客户端选择这种方法的主要原因。
一般来说,我认为值得开始了解 Observables 是如何工作的以及一些最重要的运算符(除了上面的那些,我首先想到的是 mergeMap
、filter
、reduce
- 然后还有还有许多其他)很重要,因为它们可以显着简化异步非阻塞环境中的许多任务,例如浏览器(或节点)。
【讨论】:
遗憾的是,如果我错了,请纠正我,当一切顺利时,.subscribe
签名中的“完整”参数被触发。如果发生错误,它不会被触发。我理解 Observable 的好处,我并不是反对它们。我只是想确定如何做这个基本的(也许为什么它不再是基本的好习惯了)。
是的,你是对的,complete
或 error
或者什么都没有,对于永远不会完成的 Observables。我不明白悲伤的部分。
与上面的评论相同,我开始认为我错过了一些明显的东西:在我的示例中,我应该在哪里调用 stopLoading?
@Picci 我认为他想要的最终没有完成,只有在成功后才会调用。他想要的就像是成功或失败后的封闭。检查此***.com/a/44771000/667767 以查看完成和最终完成之间的区别以上是关于Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后的主要内容,如果未能解决你的问题,请参考以下文章
Angular/RxJS 6:如何防止重复的 HTTP 请求?
rxjs-websockets - 如何封装为 Angular 6/7 服务