繁体   English   中英

RXJS 如何知道 Finalize 何时完成?

[英]RXJS How to Know When Finalize Completes?

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

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

这是它的堆栈闪电战,尽管我也会在下面发布片段: 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();
  });
}

我惊讶地发现这个例子的 output 是

开始排放

42

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

100

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

200

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

完成排放

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

serviceCall finalize 中 isLoading 的值:false

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

关于finalize

这归结为finalize是如何实现的。 我同意这可能不是很直观。 我是一个分裂派系的一部分,我相信它现在的实施方式直观的方式。

考虑一个在发出任何东西之前取消订阅的 observable。 我希望 finalize 仍会触发,但我不希望将complete的通知发送给我的观察者。

六个一个,六个另一个

通常,发生在 stream 上的最后一件事是取消订阅。 当取消订阅 stream 时,将调用 Finalize。 这发生在完成或错误发射之后。

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


尽可能避免副作用

一般来说,设置全局变量和稍后在同一pipeline中检查它们等副作用被认为是代码异味。 相反,如果您更加努力地采用 RxJS 流所倡导的功能方法,则此类问题应该会消失。


快速旁白:

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

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

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 的微任务队列中,你会注意到代码运行方式的显着差异。 在使用第一个值调用您的 subscribe 之前, isLoading已经为 false。

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


最小阻力的解决方案

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

您可以使用点击运算符来捕获complete的发射。 由于 tap 将触发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)
      })
    );
  });

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM