Search code examples
angulartypescriptrxjshttprequestswitchmap

Angular Rxjs Multiple Parallel HTTP Requests / Calls blocking navigation - Priority Cancel Queue Concurrent


i know there are similiar threads but there isnt one that provided a fitting solution for my problem.

Pre: I am navigating on the server side and on the client side at the same time. That means every navigation on the frontend with routerLink and any route which is called is accompanied by a HTTP request call to the server.

Situation: I am opening a dialog to show a form to create a position. For this position I need to show a list of elements which I am loading from the API. For each element I need to load an additional thumbnail. So I need one API call to get the 86 elements and then 86 requests to get the thumbnails. I am saving this elements within a service to prevent loading them again. Therefore I am checking if there are already elements before going to load them. The API calls are started, when I am opening the dialog.

getElementsWithThumbnails() {
    if (this.elementsSource.value.length === 0) {
        this.apiService.getElements().subscribe(
            (next) => {
                this.elementsSource.next(next);
                this.elementsSource.value.forEach((epl, index) => {
                    const format = 'JPG';
                    this.apiService.getThumbnail(epl.id, format, '400', '400', 'true').subscribe(
                        (blob) => {
                            epl.thumbnail = blob;
                        }
                    );
                });
            }
        );
    }
}

ThumbnailRequestMethod:

getThumbnail(
    elementId: string, format: string, width: string,
    height: string, sameratio: string
): Observable<SafeUrl> {
    return this.httpClient.get(
        this.apiUrl +
        `/elements/${elementId}/thumbnail?format=${format}&width=${width}&height=${height}&sameratio=${sameratio}`
        , {
            responseType: 'blob'
        }).pipe(
        map(res => {
            return this.blobToSanitizedUrl(res);
        })
    );
}

Problem: If the user decides to cancel the form and wants to navigate forward/backward - he cant, because the navigation request is on the end of the queue.

Is there any method which sets priority of the thumbnail calls on a lower one to be handle on a lesser load?

Thanks in advance.


Solution

  • The problem is that the browser has a limit to parallel requests to the same endpoint. Is the api on the same origin as the frontend? If it's not the limit is probably lower.

    The way I see it:

    1. Use urls for the thumbnails. I assume you display them in some img tags. Why not set the href and let the browser handle loading of the images. Going forward on this idea, you could use an "inview" directive to defer the loading of the images until they are actually in view (this also depends on the business and requirements).

    2. Limit the parallel requests to getThumbnail. You can do it like this (using the concurrent parameter for mergeMap):

    const format = 'JPG';
    this.apiService.getElements()
      .pipe(
        switchMap(items => {
          this.elementsSource.next(items);
          return from(items);
        }),
        mergeMap(item => {
          return this.apiService
            .getThumbnail(item.id, format, '400', '400', 'true')
            .pipe(
              tap(blob => item.thumbnail = blob)
            )
        }, 2) // max 2 requests in parallel
      )
      .subscribe();