网站搜索

angular - 如何在 Angular 中使用 rxjs 依次执行两个 http 调用,但不等待第二个完成?


我有以下要求:

  • 执行 HTTP 调用 A
  • 等A完成
  • 执行 HTTP 调用 B
  • 不要等B完成
  • 返回 HTTP 调用 A 的结果

这似乎是使用 Rxjs tap 操作符的一个好例子,因为调用 B 被视为一个“副作用”。代码如下(对该函数结果的订阅在代码的其他地方进行):

public call(): Observable<any> {
  return this.http.get('/operation/a').pipe(
    tap(() => {
      this.http.get('/operation/b');
    })
  );
}

我注意到调用 B 没有被执行,因为没有人订阅它。以下代码解决了这个问题:

public call(): Observable<any> {
  return this.http.get('/operation/a').pipe(
    tap(() => {
      this.http.get('/operation/b').subscribe();
    })
  );
}

然而,这感觉有点不舒服,因为我们现在有两个订阅:一个在 tap 内,另一个在调用此方法时。技术上这不是问题,因为 tap 内的可观察对象会完成,因此不需要取消订阅,但感觉不太对。

我不知道如何在不等待调用 B 的结果的情况下实现“发射即忘”的功能。有没有使用 Rxjs 的“正确”方法来做到这一点?

答案#1

实际上,我并没有看到实现上有什么问题。而且由于 Angular 的 HTTP 调用只会发出单个值,take(1) 是多余的。如果你担心订阅会悬挂,你可以显式地关闭它们(例如,在 ngOnDestroy() 钩子中)。更多信息请参见这里。

但是,如果你只想拥有一个订阅,你可以将 combineLatest 与 startWith 一起使用,并将其管道到第二个可观察对象。没有 startWith(null),订阅者在 /operation/b 发出之前不会接收到来自 /operation/a 的发射。

然后你可以使用 map 操作符来忽略第二个可观察对象的发射。

注意:我确信这个问题可能有更好的解决方案。以下是我能想出的最快方法

import { combineLatest, startWith } from 'rxjs';
import { map } from 'rxjs';

public call(): Observable<any> {
  return combineLatest({
    a: this.http.get('/operation/a'),
    b: this.http.get('/operation/b').pipe(startWith(null))
  }).pipe(
    map(({a, b}) => a)
  );
}

答案#2

只需将调用设为异步,等待 a,但订阅 b,然后返回 a。对 b 的调用将是异步的,因为你没有等待它。我觉得管道很让人困惑,除非必要,否则会避免使用它们。

import { firstValueFrom } from 'rxjs';

public async call(): Something {
  let a = await firstValueFrom(this.http.get('/operation/a'));
  this.http.get('/operation/b').subscribe((b) => { ... });
  return a;
}

另外,最好使用 firstValueFrom 而不是 .pipe(take(1)),但在这种情况下甚至不需要这样做。