Master Asynchronous Calls in Angular: Unlocking the Power of Observables and Promises!

Master Asynchronous Calls in Angular: Unlocking the Power of Observables and Promises!

·

5 min read

Managing asynchronous calls in Angular is an essential part of handling tasks like API requests, timers, or other delayed processes. In Angular, asynchronous operations are typically managed using Observables (from the RxJS library), Promises, or the async/await syntax. Below, I'll explain every minute detail about handling asynchronous operations in Angular with a simple example.

Key Concepts in Asynchronous Calls:

  1. Observables:

    • Angular heavily relies on Observables, which are part of the RxJS library.

    • Observables allow you to subscribe to data streams and react to changes.

    • They are lazy and do not execute until subscribed to.

  2. Promises:

    • A Promise represents a single value that may be available now or in the future.

    • Promises are resolved or rejected once, and they don’t emit multiple values like Observables.

  3. async/await:

    • async/await is a modern way of handling asynchronous code in JavaScript.

    • It's built on Promises and makes asynchronous code look synchronous, which improves readability.

Example 1: Using Observables (HttpClient API)

In Angular, when you perform HTTP requests, you use HttpClient to make asynchronous calls. The HttpClient returns an Observable, which you can subscribe to in order to get the data.

1. Set up the Angular Service:

First, create a service to handle HTTP requests.

bashCopy codeng generate service data

In the data.service.ts file, write the following code to make an HTTP request:

typescriptCopy codeimport { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  constructor(private http: HttpClient) {}

  // Method to get data from an API
  getData(): Observable<any> {
    return this.http.get('https://jsonplaceholder.typicode.com/posts');
  }
}

In this code:

  • We import HttpClient and Observable to handle asynchronous HTTP requests.

  • getData() method makes an HTTP GET request to fetch posts and returns an Observable.

2. Using the Service in a Component:

Next, inject the service in a component to use the getData() method.

bashCopy codeng generate component posts

In posts.component.ts, use the service:

typescriptCopy codeimport { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {
  posts: any = [];

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    this.dataService.getData().subscribe(
      (data) => {
        this.posts = data;
        console.log(data);
      },
      (error) => {
        console.error('Error occurred:', error);
      }
    );
  }
}
  • ngOnInit(): This lifecycle hook is used to trigger the HTTP request when the component initializes.

  • this.dataService.getData().subscribe(): Subscribes to the Observable returned by the getData() method. When the data is received, it’s stored in the posts array.

3. Displaying the Data in the Template:

In the posts.component.html, you can display the fetched posts:

htmlCopy code<div *ngIf="posts.length > 0">
  <ul>
    <li *ngFor="let post of posts">{{ post.title }}</li>
  </ul>
</div>
<div *ngIf="posts.length === 0">
  <p>Loading...</p>
</div>
  • *ngIf="posts.length > 0": Displays the list when the data is loaded.

  • *ngFor="let post of posts": Loops through each post and displays the title.

Example 2: Using async/await with Promises

If you're working with a Promise (instead of an Observable), you can use async/await to handle asynchronous calls.

1. Service Method with Promise:

Here’s an example of a service method that returns a Promise.

typescriptCopy code@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private http: HttpClient) {}

  async getData(): Promise<any> {
    const response = await this.http.get('https://jsonplaceholder.typicode.com/posts').toPromise();
    return response;
  }
}
  • async/await: The method getData() is marked as async, meaning it will return a Promise.

  • await this.http.get(...).toPromise(): The HTTP request is awaited, meaning the code will pause until the request completes.

2. Using async/await in a Component:

In the component, you can call the service method using async/await like this:

typescriptCopy codeimport { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {
  posts: any = [];

  constructor(private dataService: DataService) {}

  async ngOnInit(): Promise<void> {
    try {
      const data = await this.dataService.getData();
      this.posts = data;
    } catch (error) {
      console.error('Error occurred:', error);
    }
  }
}
  • await this.dataService.getData(): This waits for the Promise to resolve and then updates the posts array.

  • try/catch: Handle any errors that may occur during the async call.

Key Differences Between Observables and Promises:

  • Observables can emit multiple values over time, whereas Promises only return a single value or an error once.

  • Observables are lazy, meaning they do not execute until subscribed to, whereas Promises execute immediately.

  • Promises can be converted to Observables using from() or toPromise(), but not vice versa.

Example 3: Handling Multiple Asynchronous Calls

If you need to make multiple asynchronous calls, you can use forkJoin or mergeMap from RxJS to combine them.

typescriptCopy codeimport { forkJoin } from 'rxjs';
import { DataService } from './data.service';

ngOnInit(): void {
  forkJoin([
    this.dataService.getData(),
    this.dataService.getData()
  ]).subscribe(([data1, data2]) => {
    console.log(data1);
    console.log(data2);
  });
}
  • forkJoin() combines multiple Observables and emits their last values once all have completed.

Summary:

  1. Observables (RxJS) are the preferred way of handling asynchronous calls in Angular, especially for HTTP requests, with the ability to emit multiple values.

  2. Promises are simpler and are often used with async/await.

  3. Use subscribe() to handle data from Observables, and async/await to handle data from Promises.

  4. forkJoin() can be used to wait for multiple Observables to complete.