ngrx 一次处理一个请求

ngrx one request in flight at a time

上下文: Angular 4.x,RxJs 5.4.x,NgRx 4.1.1.

目标: 我想避免在多个组件请求相同数据时重复相同的 api 调用。我不想取消飞行中的请求,只是在飞行中的请求完成之前不要再提出未来的请求。

方法:商店里有一个旗帜,名字叫isFetching。在我的效果中,我检查标志以查看是否已经有一个飞行中的请求。如果是这样,那么我不提出要求。触发效果的相同操作将 isFetching 标志设置为 true。

问题: 在效果 运行 之前更新状态。所以 isFetching 在发送第一个请求之前为真。所以没有请求被发送。

示例:这是问题的简化版本:

state.ts

export interface State {
    isFetching: boolean;
}

reducer.ts

export function reducer(state: State, action: Action): State {
    switch (action.type) {
        case 'FETCH':
            return <State>{ isFetching: action.payload };
    }

    return state;
}

effect.ts

@Effect()
public fetch: Observable<Action> = this.actions.ofType('FETCH')
    .withLatestFrom(this.store.select(x => x.isFetching), (_, x) => x)
    .filter(x => !x)
    .switchMap(action =>
        this.api.getData()
        .map(data => new FetchSuccess(data))
        .catch(() => Observable.of(new FetchFailure()))
    );

想法 #1:

我可以通过在包含效果的 class 中保留一个变量来复制状态。我会在发出 api 请求时设置它,并用它来过滤掉以后的请求。

effect.ts

public isFetching = false;

@Effect()
public fetch: Observable<Action> = this.actions.ofType('FETCH')
    .filter(() => !this.isFetching)
    .switchMap(action => {
        this.isFetching = true;
        return this.api.getData()
            .map(data => new FetchSuccess(data))
            .catch(() => Observable.of(new FetchFailure()))
            .do(() => this.isFetching = false);
    });

想法 #2:

没有设置 'FETCH' 操作 isFetching。让效果发出两个动作。第一个 (IsFetching) 设置状态,第二个 (FetchSuccess | FetchFailure) 是结果。我不喜欢我需要两个动作来完成我认为我应该能够用一个动作完成的事情。优点是,如果有多个 'FETCH' 请求,则 IsFetching 操作只会更新一个状态。

似乎效果的结果是异步应用于状态的。因此,顺序同步 'FETCH' 操作将在标志设置为状态之前触发相同数量的请求。所以不要使用这个解决方案。

effect.ts

@Effect()
public fetch: Observable<Action> = this.actions.ofType('FETCH')
    .withLatestFrom(this.store.select(x => x.isFetching), (_, x) => x)
    .filter(x => !x)
    .switchMap(action =>
        this.api.getData()
            .map(data => new FetchSuccess(data))
            .catch(() => Observable.of(new FetchFailure()))
            .startWith(new IsFetching(true))
    );

问题 是否有更多 elegant/standard 方法来实现我的目标?理想情况下,我想避免在两个地方保留相同的状态信息。

一种选择是使用维护其自身状态的运算符。

例如,您可以使用 distinct,它接受第二个参数,在发射时清除运算符的内部状态:

@Effect()
public fetch: Observable<Action> = this.actions
    .ofType('FETCH')
    .distinct(() => 'FETCH', this.actions.ofType(
        'FETCH_SUCCESS',
        'FETCH_FAILURE'
    ))
    .switchMap(action => this.api.getData()
        .map(data => new FetchSuccess(data))
        .catch(() => Observable.of(new FetchFailure()));
    );

如果键选择器总是 returns 相同的值,则在处理一个操作时收到的任何 FETCH 操作都将被忽略。