2017-06-30 11 views
7

Ich habe meine eigene Meinung zu dieser Frage, aber es ist besser zu überprüfen und sicher zu wissen. Danke, dass du aufpasst und versuchst zu helfen. Hier ist es:ngRx Zustand Update und Effekte Ausführungsreihenfolge

Stellen Sie sich vor, dass wir eine Aktion versenden, die einige Zustandsänderungen auslöst und auch einige Effekte hat. Also muss unser Code zwei Dinge tun - den Zustand ändern und einige Nebenwirkungen haben. Aber was ist die Reihenfolge dieser Aufgaben? Machen wir sie synchron? Ich glaube, dass wir zuerst den Zustand ändern und dann den Nebeneffekt tun, aber besteht die Möglichkeit, dass zwischen diesen beiden Aufgaben etwas anderes passiert? So: Wir ändern den Status, erhalten dann eine Antwort auf die HTTP-Anfrage, die wir vorher gemacht haben, und behandeln sie dann, dann machen wir die Nebenwirkungen.

[Bearbeiten:] Ich habe beschlossen, hier einen Code hinzuzufügen. Und ich habe es auch sehr vereinfacht.

Zustand:

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

Aktionen:

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) {} 
} 

Reducer:

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; 
    } 
} 

Wirkung:

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

Und das ist, wie wir FetchItemAction Versand:

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]) 
    } 
} 

Wunschszenario:

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. 

Bad Szenario:

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 

Die Frage ist also einfach, wenn das schlechte Szenario tatsächlich passieren kann, oder nicht?

Antwort

6

So unser Code hat zwei Dinge zu tun - Zustand ändern und einige Nebenwirkungen zu tun. Aber was ist die Reihenfolge dieser Aufgaben? Machen wir sie synchron?

Lassen Sie uns sagen, dass wir Aktion A. Versand Wir haben einige Reduzierungen, die Aktion A. behandeln werden diese in der Reihenfolge aufgerufen werden sie in das Objekt angegeben werden, die StoreModule.provideStore() übergeben. Dann wird der Nebeneffekt, der auf Aktion A hört, als nächstes ausgelöst. Ja, es ist synchron.

ich, dass zuerst glauben, wir Zustand ändern und führen Sie dann den Nebeneffekt, aber gibt es eine Möglichkeit, dass zwischen diesen beiden Aufgaben etwas anderes passieren könnte? Wie folgt: wir ändern den Zustand, dann erhalten wir eine Antwort auf HTTP-Anfrage, die wir zuvor gemacht haben und behandeln, dann tun die Seite Effekte.

Ich habe ngrx seit Mitte letzten Jahres verwendet und ich habe nie beobachtet, dass dies der Fall ist. Was ich fand, ist, dass jedes Mal, wenn eine Aktion ausgelöst wird, sie den gesamten Zyklus durchläuft, der zuerst von den Reduzierern und dann von den Nebenwirkungen gehandhabt wird, bevor die nächste Aktion ausgeführt wird.

Ich denke, das muss der Fall sein, da redux (aus dem sich ngrx entwickelte) sich auf der Hauptseite als vorhersagbarer Statuscontainer ausgibt. Dadurch, dass unvorhersehbare asynchrone Aktionen möglich sind, können Sie nichts vorhersagen, und die Redux-Dev-Tools wären nicht sehr nützlich.

Edited # 1

Also habe ich gerade einen Test. Ich habe eine Aktion 'LONG' ausgeführt und dann würde der Nebeneffekt eine Operation ausführen, die 10 Sekunden dauert. In der Zwischenzeit war ich in der Lage, die Benutzeroberfläche weiter zu verwenden, während ich mehr Meldungen an den Staat weitergab. Schließlich wurde der Effekt für "LONG" beendet und "LONG_COMPLETE" gesendet. Ich habe mich geirrt, dass die Reduzierungen und Nebenwirkungen eine Transaktion sind.

enter image description here

sagte, dass ich denke, es ist immer noch schwer zu deuten, was los ist, weil alle Statusänderungen noch Transaktions sind. Und das ist eine gute Sache, weil wir nicht möchten, dass die Benutzeroberfläche blockiert, während auf einen lang laufenden API-Anruf gewartet wird.

Edited # 2

Also, wenn ich verstehe das richtig der Kern Ihrer Frage zu switchMap und Nebenwirkungen. Im Grunde fragen Sie, was passiert, wenn die Antwort in dem Moment zurückkommt, in dem ich den Reducer-Code ausführe, der dann den Nebeneffekt mit switchMap auslöst, um die erste Anfrage abzubrechen.

Ich kam mit einem Test, der meiner Meinung nach diese Frage beantwortet. Der Test, den ich eingerichtet habe, war, 2 Knöpfe zu erstellen. Einer hieß Quick und einer Long. Quick versendet "QUICK" und Long versendet "LONG". Der Reduzierer, der auf Quick hört, wird sofort beendet. Der Reducer, der auf Long hört, benötigt 10 Sekunden.

Ich habe einen einzigen Nebeneffekt eingerichtet, der sowohl Quick als auch Long hört. Dies täuscht vor, einen api-Aufruf zu emulieren, indem ich 'of' benutze, was mir erlaubt, eine Observable von Grund auf neu zu erstellen. Dies wird dann 5 Sekunden warten (mit. Delay) vor dem Versand 'QUICK_LONG_COMPLETE'.

Während meines Tests habe ich auf den Quick Button geklickt und dann sofort auf den langen Button geklickt.

Hier ist, was passiert ist:

  • Schnelltaste
  • 'QUICK' geklickt abgesendet wird
  • Nebeneffekt beginnt eine beobachtbare, die in 5 Sekunden abgeschlossen wird.
  • Lange Schaltfläche geklickt
  • 'long' ausgelöst wird
  • Reducer Handhabung lange dauert 10 Sekunden. Bei der 5-Sekunden-Marke ist das Original, das aus dem Nebeneffekt beobachtbar ist, vollständig, aber es wird nicht "QUICK_LONG_COMPLETE" gesendet. Weitere 5 Sekunden vergehen.
  • Nebeneffekt, der "LONG" hört, macht eine Schaltkarte, die meinen ersten Nebeneffekt aufhebt.
  • 5 Sekunden passieren und 'QUICK_LONG_COMPLETE' wird ausgelöst.

enter image description here

Deshalb switchMap nicht abzubrechen und Ihr schlimmer Fall nicht immer passieren soll.

+0

Danke für die Antwort! Ja, das letzte Szenario, das ich erwähnt habe, ist sehr unerwünscht. –

+0

Aber wie hast du diesen Effekt gemacht? Hast du setTimeout() hinzugefügt oder was? Mein Anwendungsszenario ist wie folgt: 1. Benutzer klickt auf einen Artikel 2. Wir versenden eine Aktion A mit Payload als ID dieses Artikels 3. Einige Reducer speichern diese Artikel-ID im Ladezustand (state.loading). ID) 4. Wirkung eine HTTP-Anfrage mit dieser ID machen (wir abonnieren eine Aktion und tun eine switchMap) 5. Wir erhalten die Antwort-und Versandaktion A_complete 6. mit einigen Reduzierungen speichern wir das empfangene Element in den Zustand wie this: state.items [state.loading.id] = Antwort und auch state.loading.id = null –

+0

Das einzige Problem hier sehe ich ist, dass wir die state.loading.id ändern können, aber die vorherige Antwort schneller erhalten als wir mach eine andere Anfrage. In diesem Fall in unserem Zustand unter der letzten ID wäre unser vorheriger Artikel –

Verwandte Themen