RXJS - Typescript - 等待 observable 发出直到另一个 observable<boolean> 为 false

RXJS - Typescript - Await observable emit until another observable<boolean> is false

我目前有一个看起来像这样的解决方案: 当用户在视图中导航时,将执行 api 调用以获取该特定视图的数据。 该数据稍后又用于确定当用户执行操作时是否应显示对话框。

目前该解决方案正在运行,但我可以想到当用户具有高延迟并在 api 请求完成之前执行上述操作时的潜在问题。
我不想在用户每次导航时都显示一个加载指示器,因为在使用这样的系统时,这让我很烦。当 she/he 想要执行操作时,我宁愿让用户等待。
此外,我确实有一个 observable,它指示 api 请求是否正在加载,我打算以某种方式使用它。
为清楚起见,代码看起来像这样,只是对名称进行了细微改动。

actionPerformed$ = createEffect(() => 
      this.actions$.pipe(
        ofType(performAction),
        withLatestFrom(entities$),
        mergeMap(data => {
          let payload = data[0];
          let dataFromApi = data[1];
          ...
          ...
        })

我想到的一个听起来效率不高的想法是让操作员检查条件并在加载时抛出错误,并在短暂的延迟后简单地重试。

你有更好的建议吗?我一直在寻找 delayWhen 但似乎没有这样的延迟。我宁愿有一个“门”,当 loading$ 设置为 false 时打开流,并在满足该条件时立即发出值。

提前致谢!

在此示例中,dependent$ 可观察对象将在 source$ 可观察对象发出 false 后发出一个值。请查看工作代码:https://stackblitz.com/edit/rxjs-r3yma1?devtoolsheight=33&file=index.ts

import { of, timer } from "rxjs";
import { filter, map, switchMap, take, tap } from "rxjs/operators";

const arr = [true, true, true, true, true, true, false];
const source$ = timer(0, 1000).pipe(
  map(i => arr[i]),
  tap(console.log)
);

const dependent$ = of("the other guy said false");

source$
  .pipe(
    filter(val => val === false),
    switchMap(() => dependent$),
    take(1)
  )
  .subscribe(console.log);

在此示例中,依赖的可观察对象将立即发出,因为服务尚未从 API 获取数据。然后服务将开始获取数据,一旦服务完成,依赖的 observable 将发出。您可以在 https://stackblitz.com/edit/rxjs-3ecq5d?devtoolsheight=33&file=index.ts

中查看工作代码
import { BehaviorSubject, of } from "rxjs";
import { filter, switchMap, take } from "rxjs/operators";

class Service {
  loading$ = new BehaviorSubject<boolean>(false); // Initialized false, this is the initial situation before you start fetching data from the API

  fetchData() {
    console.log("Fetching data...");
    this.loading$.next(true);
    setTimeout(() => {
      console.log("Finished fetching data");
      this.loading$.next(false);
    }, 5000);
  }
}

class Component {
  service: Service;

  constructor(service: Service) {
    this.service = service;
  }

  emitWhenReady() {
    const dependent$ = of("I'll emit when not fetching data");
    this.service.loading$
      .pipe(
        filter(loading => loading === false),
        switchMap(() => dependent$),
        take(1)
      )
      .subscribe(console.log);
  }
}

const service = new Service();
const component = new Component(service);
component.emitWhenReady(); // This guy will emit immediately because the service is not fetching data yet;
service.fetchData();
component.emitWhenReady(); // This guy will wait until the service has finished fetching data.

我觉得你可以使用 combineLatest 而不是 withLatestFrom,就像这样:

combineLatest([
  this.actions$.pipe(ofType(performAction)),
  entities$
]).pipe(
  filter(([action, entities]) => entities != null), // condition depends on entities structure
  map(([action, entities]) => {
    // doStuff
  })
);

这将等待两个 observable 至少发出一次,这将转化为加载的“实体”。过滤器只是为了安全。如果在 entitites$ 调用完成之前有一个用户操作,它还将考虑最后一个用户操作。