簡體   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