Undo observable based on payload, not effect

I have a service that makes HTTP requests to the backend, which I do not control in order to get the content of the marketing page. Sometimes I have to upload more than one ad content at a time. I can create an effect that calls the service.

@Effect() marketingContent$ = this.actions$ .ofType(LOAD_MARKETING_CONTENT) .switchMap(({ payload }) => this.marketingService.getContent(payload) .map(content => Action.LoadMarketingContentComplete(content)) ) 

This works fine, and I can call store.dispatch(Action.LoadMarketingContent('A')) .

The problem is that if I need to download more than one ad content at a time, then .switchMap will cancel the previous request.

 store.dispatch(Action.LoadMarketingContent('A')); store.dispatch(Action.LoadMarketingContent('B')); // Only `'B'` is loaded since 'A' gets canceled before it completes 

I can use .mergeMap instead of .switchMap , but then duplicate requests will not be canceled.

I can also use separate actions to download each part of the marketing content, but for this you will need to create an action and effects for each part.

Is there a way I can use .switchMap to cancel only requests for the same content (where is payload the same?) Or another way to make different requests at the same time while canceling repeated requests in the same thread?

+5
source share
2 answers

If you enter the action CANCEL_MARKETING_CONTENT , you can do something like this with mergeMap :

 @Effect() marketingContent$ = this.actions$ .ofType(LOAD_MARKETING_CONTENT) .mergeMap(({ payload }) => this.marketingService .getContent(payload) .map(content => Action.LoadMarketingContentComplete(content)) .takeUntil(this.actions$.ofType(CANCEL_MARKETING_CONTENT)) ); 

Basically, this will allow you to download as many pieces of marketing content as possible, but you need to discard any pending loads by sending the CANCEL_MARKETING_CONTENT action before sending LOAD_MARKETING_CONTENT actions.

For example, to load only piece A , you would do this:

 store.dispatch(Action.CancelMarketingContent()); store.dispatch(Action.LoadMarketingContent('A')); 

And to load both parts A and B , you would do this:

 store.dispatch(Action.CancelMarketingContent()); store.dispatch(Action.LoadMarketingContent('A')); store.dispatch(Action.LoadMarketingContent('B')); 

In fact, there is a similar, but more accurate way to do this, and it does not involve the use of another action.

You can use dispatch of the same action with the same payload as the cancel trigger. For instance:

 @Effect() marketingContent$ = this.actions$ .ofType(LOAD_MARKETING_CONTENT) .mergeMap(({ payload }) => this.marketingService .getContent(payload) .map(content => Action.LoadMarketingContentComplete(content)) .takeUntil(this.actions$ .ofType(LOAD_MARKETING_CONTENT) .skip(1) .filter(({ payload: next }) => next === payload) ) ); 

From skip memory, you need to skip the action that is currently being processed by the effect. And the answer assumes that payload is "A" or "B" , etc.

+4
source

Instead of canceling the effect, you can change the effect to get marketing content in groups and still use switchMap to cancel subsequent requests.

 @Effect() marketingContent$ = this.actions$ .ofType(LOAD_MARKETING_CONTENT) .switchMap(({ payload }) => forkJoin( payload.map(name => this.marketingService.getContent(payload)) ).map(content => Action.LoadMarketingContentComplete(content)) 

In this case, payload will be an array of content names to retrieve instead of a single name that allows you to download content in groups.

0
source

Source: https://habr.com/ru/post/1275121/


All Articles