Daniel B
Daniel B

Reputation: 8879

mergeMap cancels previous HTTP calls

I have a dialog that when opened dispatches an action to preload data for form controls inside the dialog. The action is picked up by an effect that in turn returns an array of new actions to preload the data for each id in the array that is passed to the original action.

@Effect()
preloadReferenceData$ = this.actions$
  .ofType(PRELOAD_REFDATA)
  .pipe(
    map((action: PreloadRefData) => action.payload),
    mergeMap(ids => {
      const actions: Action[] = [];
      ids.forEach((id) => actions.push(new LoadRefDataForId(id)));
      return actions;
    })
  );

Normally, I use the mergeMap operator to successfully dispatch multiple new actions from an effect, though this is the first time I'm dispatching multiple actions of the same type, and it's the first time I've experienced that the XHR calls get cancelled. I would have suspected the use of switchMap in the case where the XHR calls are cancelled, but not now when I use mergeMap.

These is the content of actions

LoadRefDataForId {payload: "2441", type: "[Refdata] Load RefData"}
LoadRefDataForId {payload: "7612", type: "[Refdata] Load RefData"}
LoadRefDataForId {payload: "9411", type: "[Refdata] Load RefData"}

and this is taken from the network log

enter image description here

The effect that handles the LoadRefDataForId action

@Effect()
loadRefDataForId$ = this.actions$
  .ofType(LOAD_REFDATA_FOR_ID)
  .pipe(
  map((action: LoadRefDataForId) => action.payload),
  switchMap((id) => this.service.loadRefData(id)
    .pipe(
    map((occupations) => {
      return new LoadRefDataForIdSuccessful({ id: id, occupations: occupations } as RefDataPayload);
    }),
    catchError((err, caught) => {
      console.log(caught);
      return caught;
    })
    ))
  );

Why are the XHR cancelled, even though I'm using mergeMap?

Upvotes: 1

Views: 2081

Answers (1)

Jota.Toledo
Jota.Toledo

Reputation: 28464

The cancelations are caused by this effect:

@Effect()
loadRefDataForId$ = this.actions$
  .ofType(LOAD_REFDATA_FOR_ID)
  .pipe(
  map((action: LoadRefDataForId) => action.payload),
  switchMap((id) => this.service.loadRefData(id)
    .pipe(
    map((occupations) => {
      return new LoadRefDataForIdSuccessful({ id: id, occupations: occupations } as RefDataPayload);
    }),
    catchError((err, caught) => {
      console.log(caught);
      return caught;
    })
    ))
  );

By using switchMap in it, you are telling that only the last LoadRefDataForId action that passes through the actions$ stream should be considered/processed.

Change it to:

@Effect()
loadRefDataForId$ = this.actions$
  .ofType(LOAD_REFDATA_FOR_ID)
  .pipe(
  map((action: LoadRefDataForId) => action.payload),
  mergeMap((id) => this.service.loadRefData(id)
    .pipe(
    map((occupations) => {
      return new LoadRefDataForIdSuccessful({ id: id, occupations: occupations } as RefDataPayload);
    }),
    catchError((err, caught) => {
      console.log(caught);
      return caught;
    })
    ))
  );

Upvotes: 4

Related Questions