Master Asynchronous Calls in Angular: Unlocking the Power of Observables and Promises!
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:
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.
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.
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
andObservable
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 thegetData()
method. When the data is received, it’s stored in theposts
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 methodgetData()
is marked asasync
, 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 theposts
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()
ortoPromise()
, 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:
Observables (RxJS) are the preferred way of handling asynchronous calls in Angular, especially for HTTP requests, with the ability to emit multiple values.
Promises are simpler and are often used with
async/await
.Use
subscribe()
to handle data from Observables, andasync/await
to handle data from Promises.forkJoin()
can be used to wait for multiple Observables to complete.