angular
http requests
asynchronous programming
promises
observable

Wait for multiple http requests to finish before running a function in angular

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Angular, the standard way to wait for multiple HTTP requests before running the next step is to combine the request observables and subscribe once to the combined result. For ordinary HttpClient calls, forkJoin is usually the right choice because HTTP observables emit once and then complete. The main thing to remember is that the combined callback runs only after every request has completed successfully.

Use forkJoin for Independent HTTP Requests

Here is the typical pattern:

typescript
1import { HttpClient } from '@angular/common/http';
2import { forkJoin } from 'rxjs';
3
4constructor(private http: HttpClient) {}
5
6loadData(): void {
7  forkJoin({
8    user: this.http.get('/api/user'),
9    settings: this.http.get('/api/settings'),
10    notifications: this.http.get('/api/notifications')
11  }).subscribe({
12    next: ({ user, settings, notifications }) => {
13      console.log(user, settings, notifications);
14      this.afterAllRequests(user, settings, notifications);
15    },
16    error: (err) => {
17      console.error('At least one request failed', err);
18    }
19  });
20}
21
22afterAllRequests(user: any, settings: any, notifications: any): void {
23  console.log('all requests finished');
24}

forkJoin waits until all inner observables complete, then emits one combined value.

That makes it a natural fit for HttpClient requests.

Why forkJoin Works Well for HTTP

Angular HttpClient calls usually:

  • emit one response value
  • complete immediately afterward

That is exactly the shape forkJoin expects. It is much closer to Promise.all than to a streaming combination.

If all requests succeed, you get one object or array containing every result. If one fails, the combined observable errors unless you handle that inside the individual streams.

Handle Errors Per Request When Needed

Sometimes you do not want one failed request to cancel the whole batch. In that case, catch errors inside each request.

typescript
1import { of, forkJoin } from 'rxjs';
2import { catchError } from 'rxjs/operators';
3
4forkJoin({
5  user: this.http.get('/api/user').pipe(catchError(() => of(null))),
6  settings: this.http.get('/api/settings').pipe(catchError(() => of(null)))
7}).subscribe(result => {
8  console.log(result);
9});

This lets the combined call finish even if one request fails, while still making the failure visible in the result.

Use switchMap for Dependent Requests

If request B depends on the result of request A, forkJoin is not the right tool. Chain the requests instead.

typescript
1this.http.get<{ id: number }>('/api/user').pipe(
2  switchMap(user => this.http.get(`/api/orders/${user.id}`))
3).subscribe(orders => {
4  console.log(orders);
5});

forkJoin is for independent requests that can run in parallel. switchMap is for dependent workflows.

Async and Await with Promises

If you prefer async and await, convert the observables to promises first. In current Angular code, that usually means firstValueFrom.

typescript
1import { firstValueFrom } from 'rxjs';
2
3async loadData(): Promise<void> {
4  const [user, settings] = await Promise.all([
5    firstValueFrom(this.http.get('/api/user')),
6    firstValueFrom(this.http.get('/api/settings'))
7  ]);
8
9  this.afterAllRequests(user, settings);
10}

This can be nice for small async workflows, but staying in RxJS with forkJoin is often more idiomatic in Angular services and components.

Common Pitfalls

The most common mistake is trying to call the follow-up function right after starting the requests, which runs too early because HTTP is asynchronous.

Another issue is using forkJoin with observables that never complete. forkJoin waits for completion, so long-lived streams such as certain live subjects or sockets will prevent it from emitting.

Developers also sometimes use nested subscriptions for parallel requests. That works, but it becomes harder to read and harder to handle errors compared with a single composed pipeline.

Finally, remember that one unhandled error inside forkJoin fails the combined observable.

Summary

  • Use forkJoin when you need several Angular HTTP requests to finish before continuing.
  • 'forkJoin works well because HttpClient observables usually emit once and complete.'
  • Catch errors inside individual requests if one failure should not cancel the whole batch.
  • Use switchMap instead when later requests depend on earlier results.
  • If you prefer promises, firstValueFrom plus Promise.all is the async and await equivalent.

Course illustration
Course illustration

All Rights Reserved.