【问题标题】:How to tell interval to wait for the last http call?如何告诉间隔等待最后一个http调用?
【发布时间】:2021-08-06 15:43:14
【问题描述】:

我说 http 需要 10 秒才能完成(有时需要 1 秒才能完成)并且每 5 秒间隔运行一次。

我希望间隔停止并等到 http 完成然后继续。

这是重现问题:

codesandbox.io

  refresh$ = interval(5 * 1000).pipe(tap(() => this.load()));

  fakehttp() {
    return timer(10 * 1000).pipe(take(1));
  }

  load() {
    this.fakehttp().subscribe((r) => {
      console.log("data");
    });
  }

  ngOnInit() {
    this.refresh$.subscribe((r) => {
      console.log("refresh!");
    });
  }

【问题讨论】:

  • timer/interval 是 RxJS 的东西吗?
  • 是的。这是重现问题。我有 http 需要 10 秒才能完成(有时需要 1 秒才能完成)并且每 5 秒间隔运行一次。我希望间隔停止并等到 http 完成然后继续。
  • 好的,只是想确保我阅读了正确的文档。
  • 我认为间隔是错误的方式。响应可能会触发一个新的请求,例如去抖动。
  • @jabaa 使用 RxJS 并没有那么难。同样的逻辑。更好的结果。我只需要阅读文档,因为我没有经常使用它。

标签: javascript angular rxjs


【解决方案1】:

您可以创建自己的 observable 并手动更新。然后在load() 中建立一个递归的timeout() 调用。这样,您总是会处理结果并然后等待 10 秒。您不会以过早的更新电话告终:

refresh$ = new Rx.Subject();

fakehttp() {
  return timer(10 * 1000).pipe(take(1));
}

load() {
  this.fakehttp().subscribe((r) => {
    console.log("data");

    //push value to observable
    refresh$.next(r);
    //schedule the next execution
    timer(10 * 1000)
      .subscribe(() => this.load());
  });
}

ngOnInit() {
  this.refresh$.subscribe((r) => {
    console.log("refresh!");
  });

  //start the update cycle
  this.load();
}

手动可观察更新方式来自this answerluisgabrial

【讨论】:

  • 我正在查看 RxJS 文档以寻找一种更好的方法来制作和更新 observables。
  • 这个解决方案的问题是你调用load函数递归而不释放最后一次调用,所以它在堆栈中打开并且可能导致内存泄漏。如果我只想加载一次怎么办?我可以做吗?我不这么认为。您将计时器与加载功能结合在一起。
  • @ShlomiLevi AFAIK,timer 运行并完成。因此,.subscribe 回调从堆栈中清除。 RxJS 不是这样吗? “如果我只想加载一次怎么办?”不是 OP 代码的情况。 OP 明确希望定期刷新。
【解决方案2】:

使用tap 触发第二个 observable 结果在多个订阅中。

选项 1

如果我正确理解了这个问题,您会在当前请求发出 5 秒后尝试重新触发 HTTP 请求。在这种情况下,呼叫的时间间隔 b/n 是动态的。在这种情况下,您会尝试在每次通知后使用BehaviorSubject 手动触发请求。

试试下面的

import { timer, BehaviorSubject, Subject } from 'rxjs';
import { switchMap, takeUntil } from 'rxjs/operators';

export class Sample implements OnInit, OnDestroy {
  timerSrc = new BehaviorSubject<number>(0);  // <-- default time
  close = new Subject<any>();  // <-- use to close open observables

  ngOnInit() {
    this.timerSrc.asObservable().pipe(
      takeUntil(this.close),
      switchMap((time: number) => timer(time)),
      switchMap(() => this.httpRequest())
    ).subscribe({
      next: (response: any) => {
        // use response
        this.timerSrc.next(5000);  // <-- call again in 5 seconds
      },
      error: (error: any) => {
        // handle error
      }
    });
  }

  ngOnDestroy() {
    this.close.next();  // <-- close open subscription 
  }
}

选项 2

如果您不介意每次连续调用都有一个 5 秒的固定计时器,您可以使用 exhaustMap 通过管道连接到一个间隔为 5 秒的计时器。在内部可观察对象(HTTP 请求)发出之前,它会忽略传入的排放。

请注意,这里不能保证每个连续的时间间隔都固定为 5 秒。它可能是 b/n 0 - 5 秒,具体取决于发出 HTTP 请求所用的时间。

import { timer, Subject } from 'rxjs';
import { exhaustMap } from 'rxjs/operators';

export class Sample implements OnInit, OnDestroy {
  close = new Subject<any>();  // <-- use to close open observables

  ngOnInit() {
    timer(0, 5000).pipe(
      takeUntil(this.close),
      exhaustMap(() => this.httpRequest())
    ).subscribe({
      next: (response: any) => {
        // use response
      },
      error: (error: any) => {
        // handle error
      }
    });
  }

  ngOnDestroy() {
    this.close.next();  // <-- close open subscription 
  }
}

【讨论】:

    猜你喜欢
    • 2010-11-08
    • 2010-11-22
    • 1970-01-01
    • 1970-01-01
    • 2020-07-29
    • 2017-12-06
    • 2014-08-16
    • 2022-01-02
    相关资源
    最近更新 更多