微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

RXJS 如何知道 Finalize 何时完成? 关于finalize尽可能避免副作用快速搁置:阻力最小的解决方案

如何解决RXJS 如何知道 Finalize 何时完成? 关于finalize尽可能避免副作用快速搁置:阻力最小的解决方案

我对 RXJS 比较陌生,所以如果这是重复的话,我深表歉意。我尝试搜索答案,但找不到任何答案,可能是因为我不知道要使用哪些搜索词。

我试图了解如何知道服务调用的 finalize 块何时完成,因为它更新了共享状态变量。

这是它的stackblitz,虽然我也会在下面发布片段:https://stackblitz.com/edit/angular-ivy-xzvkjl

我有一个 Angular 应用程序,其服务将共享 isLoading 标志设置为 true,启动 HTTP 请求,然后使用 finalizeisLoading 标志设置回 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

100

一个处理程序中 isLoading 的值:true

200

一个处理程序中 isLoading 的值:true

完成排放

完整处理程序中 isLoading 的值:true

serviceCall finalize 中 isLoading 的值:false

为什么在 finalize订阅块的完整处理程序之后调用 serviceCallngOnInit?如果不是通过已完成的处理程序,我如何知道 serviceCall 何时完成了对共享变量的操作?

解决方法

关于finalize

这归结为 finalize 的实现方式。我同意这也许不是很直观。我属于分裂派的一部分,他们认为现在实施的方式是直观的方式。

考虑一个在发出任何内容之前取消订阅的 observable。我希望最终确定仍会触发,但我不希望向我的观察者发送 complete 通知。

六个一个,另一个六个

通常,流发生的最后一件事是它被取消订阅。取消订阅流时调用 Finalize。这发生在完整或错误发射之后。

您可以将 finalize 视为可观察对象的拆卸过程中发生的事情。而观察者正在观察一个仍然存在的可观察对象的发射。


尽可能避免副作用

一般来说,设置全局变量并稍后在同一个 pipeline 中检查它们等副作用被视为代码异味。相反,如果你更倾向于 RxJS 流提倡的函数式方法,这样的问题应该会消失。


快速搁置:

定时异步事件通常会导致奇怪或意外的结果(如果你能帮忙的话,你真的不应该手动实现这种事情的部分原因)。

考虑一下当我在你的流中添加延迟时会发生什么:

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);
    }),delay(0)
  );
}

您会认为 0 毫秒的延迟应该没什么区别,但是因为每个延迟都放在 JS 的微任务队列中,您会注意到代码运行方式的显着差异。在使用第一个值调用您的订阅之前,isLoading 已经是假的。

那是因为延迟之前的一切都是同步运行的,并且会在微任务队列运行之前完成。 delay(0) 之后的所有内容都是异步运行的,并将在下一次 JS 准备好运行微任务队列时完成。


阻力最小的解决方案

这不是惯用的 RxJS,但在这种情况下,它会按照您期望的 finalize 方式工作。

您可以使用 tap 运算符来捕获 complete 发射。由于点击将在 complete 上触发,因此它应该在 subscribe 之前触发,因此适用于您的用例。

function serviceCall() {
  const setIsLoading = bool => (_ = null) => this.isLoading = bool;
  
  return defer(() => {
    setIsLoading(true)();
    return this.httpCall().pipe(
      tap({
        next: console.log,error: setIsLoading(false),complete: setIsLoading(false)
      })
    );
  });

}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。