The application I am working on uses a NgRx Store, and I am triggering my API calls via @ngrx/effects. I think my problem can be best explained by imagining a simple to-do list. The following is a simplification of the current state of one of my effects.
persistItem$ = createEffect(() =>
this.actions$.pipe(
ofType(ListActions.ActionType.PersistItem),
mergeMap(({ listItem }) => {
return this.listApiService.modifyListItem(listItem).pipe(
map((updatedItem) => ListApiActions.persistItemSuccess({ updatedItem })),
catchError((error) => of(ListApiActions.persistItemFailure({ failedItem: mergeItemWithError(listItem, error) })))
);
})
)
);
When the user checks an item from the list and instantly unchecks it again (because they f.e. made a mistake) now two parallel requests are on their way. Since the backend wants to prevent multiple users from overwriting other changes, versioning is applied. Due to this the second request will fail, because it still contains the old version number.
So for this to work the client has to wait for the request to finish and afterwards send the second request.
The problem with sending only one request at a time is, that this would also apply if two seperate list items are edited, which should be possible to reduce unnecessary waiting time.
So to summarize my wanted behaviour would be to only wait if there already is a pending request for the same list item. Otherwise, the client should just send the request.
Edit: This is the html template for the list.
<ul>
<li *ngFor="let item of listItems$ | ngrxPush">
{{ item.text }} <button (click)="toggleDone(item)">{{ item.done ? 'Undo' : 'Done' }}</button>
</li>
</ul>
CodePudding user response:
You can achieve this by combining some RxJS operators.
The main one being groupBy
, where you'll split the stream into multiple groups - in your case this will be a todo item. Within each group you can use concatMap
to send the requests in order.
For more info and a demo, see the following talk by Mike Ryan and Sam Julien.