带有 ngrx 存储的 Angular Rxjs 可观察链

标签 angular typescript rxjs

我将 Angular 7 与 ngrx 商店一起使用。该商店包含应用程序状态,我在 OnInit 上的应用程序组件中订阅了它。存储中有几个变量,它们是可互换的(用按钮交换)。

这是我在组件中的示例代码。

this.appState.getGasStationSushi().pipe(switchMap((sushi) => {
  this.gsSushi = sushi;
  return this.appState.getStarbucksSushi();
}), switchMap((sushi) => {
  this.sbSushi = sushi;
  return this.service.compare(this.gsSushi, this.sbSushi);
}).subscribe((result)=>{
  console.log(result);
}));

在 View 中单击按钮,用户可以更改两个 sushi 值,这会导致最后一次订阅调用两次,这是有道理的 (RxJS)。我可以删除 switchMap 并编写类似

的内容
-- gas station sushi susbcription 
   -- star bucks sushi susbcription 
      -- compare

我不是很喜欢这个,我相信肯定有一个 rxjs/operator 之类的。有人可以提出建议吗?

此外,尝试了 forkjoin,但对于 ngrx store 来说,似乎需要调用 firstlast,如下所示。上面的说法可以引用forkjoinWithstore

const $sushiObs = [
  this.appState.getGasStationSushi().pipe(first()),
  this.appState.getStarbucksSushi().pipe(first())
];
forkjoin($sushiObs).subscribe((result) => {
  console.log(result);
});

如果我使用上述模式,订阅会在第一次触发但之后就不会触发。

最佳答案

首先,这是一个working example在 stackblitz 上。

我没有使用商店,而是创建了一个返回可观察对象的模拟类 SushiState

class SushiState {
  private _gas = new BehaviorSubject(1);
  private _starbucks = new BehaviorSubject(1);

  public get gas() {
    return this._gas.asObservable();
  }
  public get starbucks() {
    return this._gas.asObservable();
  }

  public increaseSushi(n = 1) {
    this._gas.next(this._gas.value + n);
    this._starbucks.next(this._starbucks.value + n);
  }

  public static compareSushi(g: number, s: number): string {
    return `gas is ${g}, starbucks is ${s}`;
  }
}

至于组件,这里是代码。

export class AppComponent implements OnInit {
  state: SushiState;

  gasSushi: Observable<number>;
  sbSushi: Observable<number>;

  combined: string;
  combinedTimes = 0;
  zipped: string;
  zippedTimes = 0;

  ngOnInit() {
    this.state = new SushiState;
    this.gasSushi = this.state.gas;
    this.sbSushi = this.state.gas;

    const combined = combineLatest(
      this.gasSushi,
      this.sbSushi,
    ).pipe(
      tap((sushi) => {
        console.log('combined', sushi);
        this.combinedTimes++;
      }),
      map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
    );
    combined.subscribe(result => this.combined = result);

    const zipped = zip(
      this.gasSushi,
      this.sbSushi,
    ).pipe(
      tap((sushi) => {
        console.log('zipped', sushi);
        this.zippedTimes++;
      }),
      map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
    );
    zipped.subscribe(result => this.zipped = result);
  }

  increaseSushi() {
    this.state.increaseSushi();
  }

}

如果您在 stackblitz 上运行它并检查控制台,您将看到以下行为:

console output

如果我们使用 combined latest,我们分别组合 observables 并且只关心最新的状态,导致 console.log 的 2 次调用。

我们可以改为使用 zip,它会等待两个 observable 发出,然后再生成输出。这看起来非常适合我们的“增加两者”按钮,但有一个问题:如果 starbucksSushi 单独增加(可能来自应用程序的不同部分),压缩 版本也将等待加油站寿司更新。

建议第三种解决方案,您可以使用 combineLatest 组合寿司柜台,然后使用 debounceTime 运算符在发出输出之前等待一定的毫秒数。

const debounced = zip(
  this.gasSushi,
  this.sbSushi,
).pipe(
  tap((sushi) => {
    console.log('debounced', sushi);
    this.debouncedTimes++;
  }),
  map((sushi) => SushiState.compareSushi(sushi[0], sushi[1])),
  debounceTime(100),
);
debounced.subscribe(result => this.debounced = result);

这将对所有来源的变化使用react,但不会超过 100ms

最后,您必须执行 first() 的原因:

forkJoin 在 observables 完成后加入它们(这只能发生一次,所以它不适合连续流)并且更适合“类似 promise ”的工作,例如HTTP 调用、流程完成等。顺便说一下,如果您只从流中获取一个元素,则生成的流会在单次发射后完成。

附言

我建议使用 async 管道来处理可观察对象(就像我处理属性一样

gasSushi: Observable<number>;
sbSushi: Observable<number>;

然后在模板内部

<div>
  <h3>starbucks sushi</h3>
  <p>{{sbSushi | async}}</p>
</div>

代替

result => this.zipped = result

我在此示例中同时使用了两者,因此您可以比较它们。根据我的经验,使用可观察对象会变得更加容易,一旦您提前停止转换“去观察”它们并且只允许 async 管道完成它的工作。

最重要的是,如果您在组件中的某处使用了subscribe,那么当组件被销毁时您应该unsubscribe——这一点也不难,但是如果我们永远不要显式订阅,并允许 async 管道进行订阅,它还为我们处理销毁 :)

关于带有 ngrx 存储的 Angular Rxjs 可观察链,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53963571/

相关文章:

javascript - 将所有双方括号替换为 Angular 或 Java 脚本中的自定义输入标签

angular - 为网络和 ionic 应用程序设置不同的 'click_action'

Angular 6 - ng-dirty 和 ng-invalid 未按预期工作

javascript - 单击另一个输入后如何防止我的日期选择器重新聚焦

javascript - 数组的 typescript 编译错误

javascript - 服务无法正常工作时可观察到的主题

angularjs - 在 wijmo 网格 angular2 的开头添加新的空行

typescript - 当参数的类型是泛型时,是否可以在 typescript 中为参数设置默认值?

javascript - 为什么 RxJS 或 Angular Observable 订阅方法需要上下文?

rxjs - 类型错误 : You provided an invalid object where a stream was expected