I'm still new to RXJS and trying to get my head around it. The task is as follows:
A user can add assets to a collection, and he might click faster than the backend can handle the requests, so that an error might occur because the lockVersion of the collection conflicts with the one sent by the client (not updated yet). A hacky solution would be to reload the collection when the 409 error occurs, but I thought a more elegant solution would a be a request queue.
Also, we will will have to handle justified 409 errors, when two users are meddling with the same collection, so a queue will come in handy then (reload the collection, re-add the item to the queue).
Here's my naive approach, which actually works:
export class RequestQueue<T> {
private items: T[] = [];
private queue$: Subject<boolean> = new Subject<boolean>();
private dispatcher: Subject<T> = new Subject<T>();
constructor(
private readonly requestHandler: (v: T) => Observable<unknown>,
private readonly errorHandler: (e: HttpErrorResponse) => Observable<boolean>
) {
this.initQueue();
}
public add(item: T) {
if (this.items.push(item) == 1) {
this.dispatcher.next(this.items.shift());
}
return this.queue$;
}
public destroy () {
this.dispatcher.complete();
}
private initQueue() {
this.dispatcher.pipe(
flatMap(item => {
return this.requestHandler(item)
}),
tap(r => {
if (this.items.length > 0) {
this.dispatcher.next(this.items.shift());
} else {
this.queue$.next(true);
}
}),
catchError((error: HttpErrorResponse) => {
let error$ = this.errorHandler(error);
this.initQueue(); //<--- please note this line
return error$;
})
).subscribe();
}
}
As you can see, I am holding an array which can be filled up with items as much as the user feels like clicking. When the first item is added, the dispatcher emits it as its first event (value). After every successful request (collection updated), it shifts the next item from the array and emits it. This happens until the array is empty. The queue$ observer then emits 'true' to notify the caller of the queue, that it is done.
This actually works, and the 409 error never happens again because of a fast-clicking user. The trouble starts, when another error (e.g. 404) occurs. The error handler pops up a message, the user clicks ok, and the pipe of the dispatcher is dead. The next click to add an item to the collection won't cause anything, unless I initialize the queue (re-pipe to the dispatcher) again.
This doesn't feel like an elegant solution at all. I'm not quite sure why this happens, but I would really like to understand it. Any explanation or suggestion for a better solution is highly appreciated.