angulartypescriptangular-routerangular-httpclient

Resolving HTTP Request Pending State During Route Navigation


I currently have the issue that my HTTP requests are stuck in a "pending" state (for more than 10 seconds until it starts the request).

This issue is especially common if I navigate to other pages with Angular's Route Navigation (routerLink='/dashboard/contact as an example) - but it also occurs if I visit a page in the browser directly.

The API request getServerData gets fired if a page gets loaded (constructor method) or if route navigation is called - it's necessary for the page's content.

As you can see in my below code, I also have a simple caching system to avoid many HTTP requests, but that works as expected and is not the problem here.

Dashboard-Component, which calls the Service function:

getServerData(): void {
    if (!this.dataService.active_guild) { return; }
    if (!window.location.href.endsWith('/dashboard')) { return; }

    forkJoin({guildUsage: this.apiService.getGuildUsage(100),
              moduleStatus: this.apiService.getModuleStatus(this.dataService.active_guild!.id)})
      .subscribe({
        next: ({ guildUsage, moduleStatus }: { guildUsage: SliderItems[], moduleStatus: TasksCompletionList }): void => {
          this.updateTasks(moduleStatus);
          this.servers = guildUsage;

          this.dataService.isLoading = false;

          if (moduleStatus['task_1'].cached) { return; }
          localStorage.setItem('moduleStatus', JSON.stringify(moduleStatus));
          localStorage.setItem('moduleStatusTimestamp', Date.now().toString());
        },
        error: (err: HttpErrorResponse): void => {
          if (err.status === 403) {
            this.dataService.redirectLoginError('FORBIDDEN');
            return;
          } else if (err.status === 429) {
            this.dataService.redirectLoginError('REQUESTS');
            return;
          } else if (err.status === 0) {
            this.dataService.redirectLoginError('OFFLINE');
            return;
          }
          this.dataService.isLoading = false;
        }
    });
  }

Api-Service, which makes the HTTP request:

getGuildUsage(limit: number): Observable<SliderItems[]> {
    return this.http.get<SliderItems[]>(`${this.API_URL}/stats/guilds_usage` + (limit ? `?limit=${limit}` : ''));
  }

getModuleStatus(guild_id: string): Observable<TasksCompletionList> {
    const moduleStatusTimestamp: string | null = localStorage.getItem('moduleStatusTimestamp');
    const moduleStatus: string | null = localStorage.getItem('moduleStatus');

    try {
      if (moduleStatusTimestamp && moduleStatus) {
        const timestamp: number = parseInt(moduleStatusTimestamp);
        const module: TasksCompletionList = JSON.parse(moduleStatus);

        // check if module status cache is younger than 1 minute
        if (Date.now() - timestamp < 60000 && module['task_1'].guild_id === guild_id) {
          module['task_1'].cached = true;
          return of(module);
        }
      }
    } catch (error) { console.error('Cache reading error:', error); }

    return this.http.get<TasksCompletionList>(`${this.API_URL}/progress/modules?guild_id=${guild_id}`,
      { headers: this.authService.headers });
  }

My API is also answering very fast, so I don't think that is the issue here.


Solution

  • For people who have the same issue:

    It was fixed by using the ngOnDestroy event of Angular. You need to unsubscribe from these requests because they are not canceled otherwise:

    Example:

    export class MyComponent implements OnDestroy {
       private subscriptions: Subscription[] = []
       
       ngOnDestroy(): void {
        this.subscriptions.forEach(s => s.unsubscribe());
      }
    
      myApiFunction(feature_id: number, vote: boolean): void {
        const feature_vote: Subscription = this.apiService.sendFeatureVote().subscribe({
          next: (_data: any): void => {
            // do something
          },
          error: (error: HttpErrorResponse): void => {
            // do something
          }
        });
    
        this.subscriptions.push(feature_vote);
      }
    }