如何防止并发执行

How to prevent concurrent effect execution

我有一个由效果调用的昂贵计算。我现在想确保永远不会同时调用此计算,即如果在第一次调用仍在 运行 时第二次调用它,则应忽略第二次调用。

我解决这个问题的方法是创建 2 个动作:计算和 setLoading。

@Effect()
calculate$ = this.updates$
  .whenAction(CALCULATE)
  .flatMap(data => {
    console.debug('LOADING', data.state.loading);
    if (!data.state.loading) {
      this.store.dispatch(Actions.setLoading(true));
      await DO_THE_EXPENSIVE_CALCULATION();
      this.store.dispatch(Actions.setLoading(false));
    }
  });

和Actions.setLoading显然设置state.loading。但是,如果我连续开始计算 2 次:

store.dispatch(Actions.calculate());
store.dispatch(Actions.calculate());

输出是

LOADING false
LOADING false

因此,昂贵的计算被执行了两次。 我该如何防止这种情况?

您可能会看到 LOADING false 两次,因为 Action.setLoading 尚未执行。这很有可能取决于调度和操作的 synchrony/asynchrony。最好不要对此做出假设。

一般来说,如果您想限制同时执行的多个操作 time/execute 一次只能执行一个操作,您可以在 rxjs v4/v5 中使用许多运算符:

  1. flatMapWithMaxConcurrent|mergeMap :将同时订阅参数化的最大 observables,保留剩余 observables 的缓冲区以进行订阅,并在 slot 时订阅它们变得可用。因此没有损失。

  2. flatMapFirst|exhaustMap :在给定时间只会订阅一个可观察对象。当前可观察对象正在执行时出现的可观察对象将丢失。当当前的observable完成后,可以订阅新的observable。

  3. concatMap :一次只订阅一个可观察对象。将保留剩余可观察对象的缓冲区以供订阅,并在当前可观察对象完成时按顺序进行订阅。因此没有损失。

您还可以复习以下问题:

总而言之,也许这样的事情对您有用:

@Effect()
calculate$ = this.updates$
  .whenAction(CALCULATE)
  .exhaustMap(data => DO_THE_EXPENSIVE_CALCULATION())
  ;

我在这里假设 DO_THE_EXPENSIVE_CALCULATION() returns 一个承诺(也可以是一个可观察的)。