Dmy*_*ych 15 javascript ngrx ngrx-effects angular ngrx-store
我对这个问题有自己的看法,但最好仔细检查并确定.感谢您的关注并尝试提供帮助.这里是:
想象一下,我们正在调度一个触发一些状态变化的动作,并且还附加了一些效果.所以我们的代码必须做两件事 - 改变状态并做一些副作用.但这些任务的顺序是什么?我们是在同步吗?我相信,首先,我们改变状态,然后做副作用,但有可能,这两个任务之间可能会发生其他事情吗?像这样:我们改变状态,然后在我们之前做过的HTTP请求上得到一些响应并处理它,然后做副作用.
[编辑:]我决定在这里添加一些代码.而且我也简化了它.
州:
export interface ApplicationState {
loadingItemId: string;
items: {[itemId: string]: ItemModel}
}
Run Code Online (Sandbox Code Playgroud)
操作:
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) {}
}
Run Code Online (Sandbox Code Playgroud)
减速器:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
影响:
@Effect()
FetchItemAction$: Observable<Action> = this.actions$
.ofType('FETCH_ITEM')
.switchMap((action: FetchItemAction) => this.httpService.fetchItem(action.payload))
.map((item: ItemModel) => new FetchItemSuccessAction(item));
Run Code Online (Sandbox Code Playgroud)
这就是我们派遣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])
}
}
Run Code Online (Sandbox Code Playgroud)
期望的场景:
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.
Run Code Online (Sandbox Code Playgroud)
不好的情况:
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
Run Code Online (Sandbox Code Playgroud)
所以问题是,如果不好的情况真的发生了吗?
see*_*ode 17
所以我们的代码必须做两件事 - 改变状态并做一些副作用.但这些任务的顺序是什么?我们是在同步吗?
假设我们发送动作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'
}
)
});
Run Code Online (Sandbox Code Playgroud)
在测试期间,我点击了快速按钮,然后立即点击了长按钮.
这是发生了什么:
因此,switchMap确实取消了,不应该发生不良情况.