Angular RXJS如何知道Finalize何时完成?
我对RXJS比较陌生,所以如果这是重复的话,我很抱歉。我试图搜索答案,但找不到任何答案,可能是因为我不知道该使用哪个搜索词 我试图理解如何知道服务调用的finalize块何时完成,因为它更新了一个共享状态变量 下面是它的stackblitz,不过我也会在下面发布一些片段: 我有一个Angular应用程序,其服务将sharedAngular RXJS如何知道Finalize何时完成?,angular,rxjs,observable,order-of-execution,Angular,Rxjs,Observable,Order Of Execution,我对RXJS比较陌生,所以如果这是重复的话,我很抱歉。我试图搜索答案,但找不到任何答案,可能是因为我不知道该使用哪个搜索词 我试图理解如何知道服务调用的finalize块何时完成,因为它更新了一个共享状态变量 下面是它的stackblitz,不过我也会在下面发布一些片段: 我有一个Angular应用程序,其服务将sharedisLoading标志设置为true,启动HTTP请求,然后使用finalize将isLoading标志设置回false,这样无论成功与否,检查isLoading标志的项目知
isLoading
标志设置为true,启动HTTP请求,然后使用finalize
将isLoading
标志设置回false,这样无论成功与否,检查isLoading
标志的项目知道HTTP请求不再处理
我已将该场景简化为单独的方法,而不是单独的类:
isLoading = false;
public ngOnInit() {
this.serviceCall().subscribe(
next => {
console.log("value of isLoading in next handler: " + this.isLoading);
},
err => {
console.log("value of isLoading in error handler: " + this.isLoading);
},
() => {
console.log("value of isLoading in complete handler: " + this.isLoading);
}
);
}
private serviceCall() {
this.isLoading = true;
return this.httpCall().pipe(
tap(value => console.log(value)),
finalize(() => {
this.isLoading = false;
console.log("Value of isLoading in serviceCall finalize: " + this.isLoading);
})
);
}
private httpCall() {
return new Observable(subscriber => {
console.log("Starting emissions");
subscriber.next(42);
subscriber.next(100);
subscriber.next(200);
console.log("Completing emissions");
subscriber.complete();
});
}
我惊讶地发现这个例子的输出是
开始排放
42
下一个处理程序中isLoading的值:true
一百
下一个处理程序中isLoading的值:true
二百
下一个处理程序中isLoading的值:true
完成排放
完整处理程序中isLoading的值:true
serviceCall finalize中isLoading的值:false
为什么在ngOnInit
的subscribe块的完整处理程序之后调用serviceCall
的finalize
?如果不是通过已完成的处理程序,serviceCall
何时完成了对共享变量的操作,我又怎么知道呢?关于finalize
这取决于如何实现finalize
。我同意这可能不是很直观。我是一个分裂派别的一部分,该派别相信现在的实施方式是直觉的方式
考虑一个在发射任何东西之前未被订阅的可观测对象。我希望finalize仍然会触发,但我不希望向我的观察者发送完整的
通知
一个六个,另一个半打
通常,流发生的最后一件事是它被取消订阅。取消订阅流时调用Finalize。这在完全或错误发射后发生
你可以把finalize想象成在拆除一个可观察物体的过程中发生的事情。而观察者正在观察仍然存在的可观察物的发射
尽可能避免副作用 通常,设置全局变量并稍后在同一
管道中检查这些变量等副作用被视为代码气味。相反,如果您更加努力地学习RxJS streams提倡的功能性方法,那么类似的问题应该会消失
旁白:
对异步事件进行计时通常会导致奇怪或意外的结果(如果可以的话,部分原因是您确实不应该手动实现这种事情)
考虑一下当我向您的流中添加延迟时会发生什么:
private serviceCall(){
this.isLoading=true;
返回此.httpCall().pipe(
点击(值=>console.log(值)),
完成(()=>{
this.isLoading=false;
log(“serviceCall finalize中isLoading的值:“+this.isLoading”);
}),
延迟(0)
);
}
您可能会认为0毫秒的延迟应该没有什么区别,但是因为每个延迟都被放到JS的微任务队列中,所以您会注意到代码运行方式的显著差异。在使用第一个值调用subscribe之前,isLoading
将已为false
这是因为延迟之前的一切都是同步运行的,并且将在微任务队列运行之前完成。延迟(0)
之后的所有操作都是异步运行的,并将在下次JS准备运行微任务队列时完成
最小阻力解 这不是惯用的RxJS,但在本例中,它将以您期望的方式工作 您可以使用tap操作符捕捉
完成的发射。由于点击将在complete
时触发,因此应在subscribe
之前触发,因此适用于您的用例
函数serviceCall(){
const setIsLoading=bool=>(=null)=>this.isLoading=bool;
返回延迟(()=>{
设置加载(真)();
返回此.httpCall().pipe(
水龙头({
下一步:console.log,
错误:setIsLoading(错误),
完成:设置加载(错误)
})
);
});
}
感谢您帮助我了解finalize的本质!但是,使用您提供的解决方案,我不再获得finalize块所带来的好处,即在成功和错误情况下更新isLoading值。如果我调整您的解决方案,使管道还包含一个catchError,该catchError的代码与清除标志并重新抛出错误的代码相同,那么我将有一个完整的解决方案。虽然与最终定稿相比,它看起来很混乱。我确实想解决代码的味道,但现在我只关注将AngularJS转换为AngularJS,没有太多的逻辑更改。你也可以在tap操作符中看到错误,这是值得的(你不能处理它们,但如果你愿意,可以在其他地方这样做)。我已经为您更新了解决方案。实际上,再快速更新一次,您可能希望在订阅serviceCall()
时而不是调用时设置this.isLoading=true
。根据以后serviceCall()的组成方式,您可以在订阅之前很长一段时间创建可观察对象(例如,与concat
运算符通用)Defer
在订阅后只需调用其工厂函数,因此它是解决该问题的快速方法。