繁体   English   中英

ngRx状态更新和效果执行顺序

[英]ngRx state update and Effects execution order

我对这个问题有自己的看法,但最好仔细检查并确定。 感谢您的关注并尝试提供帮助。 这里是:

想象一下,我们正在调度一个触发一些状态变化的动作,并且还附加了一些效果。 所以我们的代码必须做两件事 - 改变状态并做一些副作用。 但这些任务的顺序是什么? 我们是在同步吗? 我相信,首先,我们改变状态,然后做副作用,但有可能,这两个任务之间可能会发生其他事情吗? 像这样:我们改变状态,然后在我们之前做过的HTTP请求上得到一些响应并处理它,然后做副作用。

[编辑:]我决定在这里添加一些代码。 而且我也简化了它。

州:

export interface ApplicationState {
    loadingItemId: string;
    items: {[itemId: string]: ItemModel}
}

操作:

export class FetchItemAction implements  Action {
  readonly type = 'FETCH_ITEM';
  constructor(public payload: string) {}
}

export class FetchItemSuccessAction implements  Action {
  readonly type = 'FETCH_ITEM_SUCCESS';
  constructor(public payload: ItemModel) {}
}

减速器:

export function reducer(state: ApplicationState, action: any) {
    const newState = _.cloneDeep(state);
    switch(action.type) {
        case 'FETCH_ITEM':
            newState.loadingItemId = action.payload;
            return newState;
        case 'FETCH_ITEM_SUCCESS':
            newState.items[newState.loadingItemId] = action.payload;
            newState.loadingItemId = null;
            return newState;
        default:
            return state;
    }
}

影响:

@Effect()
  FetchItemAction$: Observable<Action> = this.actions$
    .ofType('FETCH_ITEM')
    .switchMap((action: FetchItemAction) => this.httpService.fetchItem(action.payload))
    .map((item: ItemModel) => new FetchItemSuccessAction(item));

这就是我们派遣FetchItemAction的方式:

export class ItemComponent {
    item$: Observable<ItemModel>;
    itemId$: Observable<string>;

    constructor(private route: ActivatedRoute,
                private store: Store<ApplicationState>) {

        this.itemId$ = this.route.params.map(params => params.itemId);

        itemId$.subscribe(itemId => this.store.dispatch(new FetchItemAction(itemId)));

        this.item$ = this.store.select(state => state.items)
            .combineLatest(itemId$)
            .map(([items, itemId]: [{[itemId: string]: ItemModel}]) => items[itemId])
    }
}

期望的场景:

User clicks on itemUrl_1;
we store itemId_1 as loadingItemId;
make the request_1;
user clicks on itemUrl_2;
we store itemId_2 as loadingItemId;
switchMap operator in our effect cancells previous request_1 and makes request_2;
get the item_2 in response;
store it under key itemId_2 and make loadingItemId = null.

不好的情况:

User clicks on itemUrl_1;
we store itemId_1 as loadingItemId;
make the request_1;
user clicks on itemUrl_2;
we store itemId_2 as loadingItemId;  
we receive the response_1 before we made the new request_2 but after loadingItemId changed;
we store the item_1 from the response_1 under the key itemId_2;
make loadingItemId = null;
only here our effect works and we make request_2;
get item_2 in the response_2;
try to store it under key null and get an error

所以问题是,如果不好的情况真的发生了吗?

所以我们的代码必须做两件事 - 改变状态并做一些副作用。 但这些任务的顺序是什么? 我们是在同步吗?

假设我们发送动作A.我们有一些处理动作A的reducers。它们将按照传递给StoreModule.provideStore()的对象中指定的顺序被调用。 然后,听取动作A的副作用将在下一次触发。 是的,它是同步的。

我相信,首先,我们改变状态,然后做副作用,但有可能,这两个任务之间可能会发生其他事情吗? 像这样:我们改变状态,然后在我们之前做过的HTTP请求上得到一些响应并处理它,然后做副作用。

从去年中期开始我一直在使用ngrx,我从来没有注意到这种情况。 我发现,每次调度一个动作时,它都会经历由减速器首先处理的整个循环,然后在处理下一个动作之前通过副作用。

我认为这是必须的,因为redux(ngrx从中演变而来)将自己称为主页上可预测的状态容器。 通过允许发生不可预测的异步操作,您将无法预测任何内容,并且redux dev工具不会非常有用。

编辑#1

所以我刚做了一个测试。 我运行了一个'LONG'动作,然后副作用将运行一个需要10秒的操作。 与此同时,我可以继续使用UI,同时向州提供更多的调度。 最后,'LONG'的效果已完成并发送'LONG_COMPLETE'。 我认为减速器和副作用是一种交易是错误的。

在此输入图像描述

这就是说我认为仍然很容易预测发生了什么,因为所有州的变化仍然是交易性的。 这是一件好事,因为我们不希望UI在等待长时间运行的api调用时阻塞。

编辑#2

因此,如果我理解正确,你的问题的核心是关于switchMap和副作用。 基本上你问的是,如果响应在我运行reducer代码时返回,然后使用switchMap运行副作用以取消第一个请求。

我想出了一个我认为可以回答这个问题的测试。 我设置的测试是创建2个按钮。 一个叫Quick,一个叫Long。 Quick将发送'QUICK',Long将发送'LONG'。 收听Quick的reducer将立即完成。 收听Long的reducer需要10秒钟才能完成。

我设置了一个可以同时监听Quick和Long的单一副作用。 这假装通过使用'of'来模拟api调用,这让我从头开始创建一个observable。 然后在调度'QUICK_LONG_COMPLETE'之前等待5秒(使用.delay)。

  @Effect()
    long$: Observable<Action> = this.actions$
    .ofType('QUICK', 'LONG')
    .map(toPayload)
    .switchMap(() => {
      return of('').delay(5000).mapTo(
        {
          type: 'QUICK_LONG_COMPLETE'
        }
      )
    });

在测试期间,我点击了快速按钮,然后立即点击了长按钮。

这是发生了什么:

  • 单击快速按钮
  • 发送'QUICK'
  • 副作用开始一个可在5秒内完成的观察。
  • 单击长按钮
  • 'LONG'被派遣
  • 减速机处理时间长达10秒。 在5秒标记处,副作用中的原始可观察量完成,但不会发送'QUICK_LONG_COMPLETE'。 又过了5秒钟。
  • 听“LONG”的副作用会使switchmap取消我的第一个副作用。
  • 5秒后传递,并调度'QUICK_LONG_COMPLETE'。

在此输入图像描述

因此,switchMap确实取消了,不应该发生不良情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM