async/await in Angular Http: improve the developer experience using async/await

Async/Await is a technique to write asynchronous code avoiding callbacks. When you make a Http request in javascript, you decide what to do with the result in a callback. If you’re an expert developer, it’s easy to manage asynchronous code, but for novices it can be easier to reason synchronously. The last version of JavaScript introduced this concept of async/await, where you can say: “ok, stop and wait for a response to come back from the server, then continue to execute the code”. Typescript supports async/await so let’s see how we can take advantage of this in Angular and why.

If you want to go straight to the code, then grab this gist:
 An Angular 4 Http Async/Await wrapper with preferences pre-loading and JWT token support
 If you want to understand how it works, here we go…

Summary

  1. Two reasons to use async/await
  2. HttpService V1: a simple wrapper
  3. HttpService V2: getting the JSON directly
  4. HttpService V3: the complete version with preferences pre-loading and JWT token support

Two reasons to use async/await

Fact: using async/await make you write better code

Using this technique will let you write a lot less code and writing less lines of code is enough to be more robust against bugs (the less you write, the less you can introduce bugs :-) ). After you master this technique, it becomes easier to reason about the code flow: everything seems to be synchronous again!

At my company we were able to reduce the number of code related to Http requests by 40%. Now everything is more readable and more maintainable.

Fact: using subscribe is too verbose

By using async await you can reduce the number of lines of code significantly.

Let’s make an example.

This is what you’re used to:

this.backendService.getData().subscribe((res)=>{ 
if(res and res.json()){
let result = res.json();
}
})

Compare this code with the following:

let result = await this.backendService.getData()

No callbacks and as you can see it’s a lot less lines of code to accomplish the same result. But how does it works?

By using the await keyword, the call will wait until the server responds. After the response, the lines of code following the let result = await this.backendService.getData() line get executed.


HttpService V1: a simple wrapper

The first version of the async/await service is a simple wrapper around the built-in Angular Http service.

First add this line to the main.ts file:

import 'rxjs/add/operator/toPromise';

Then create a service ng g s http-service and paste this code:

import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
  @Injectable()
export class HttpService {
constructor(private http: Http) {
}

get(url:string) {
return this.http.get(url).toPromise()
}

// same for post, put, delete, head
}

So let’s explain the code:

  • We needed to transform the observer from the Angular Http get into a promise
  • Luckily RxObservers have a toPromise operator
  • to use the operator, we need to import it somewhere. We imported it in the main.ts file
  • We created a service and wrapped the Angular Http service
  • We created a get method that actually transforms the Observer into a Promise

We can use this service as shown below:

// my class
constructor(private asyncHttp:HttpService){}

async retrieveData(){
let result = await this.asyncHttp.get('http://ww.example.com/backendData')
console.log(result)
}

HttpService V2: getting the JSON directly

Almost always we need to work with the json version of the result. In Angular we use the response.json()method. We can modify the wrapper above to always return the json:

import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
  @Injectable()
export class HttpService {
constructor(private http: Http) {
}

get(url:string) {
return new Promise((resolve, reject) => {
this.http.get(url).toPromise().then((res) => {
try {
if (res.json()) {
resolve(res.json())
}
} catch(e) {
reject(e)
}
}).catch((e) => {
reject(e)
})
})
}

// same for post, put, delete, head
}

The beautiful thing about async/await is that you can look for errors using a simple try/catch block, as you usually do for synchronous code

// my class
constructor(private asyncHttp:HttpService){}

async retrieveData(){
try {
let result = await this.asyncHttp.get('http://ww.example.com/backendData')
console.log(result) // result is now the json ;-)
} catch(e){
console.log(e); // here you get any error if the request was not successful
}
}

This should be enough to work seamlessly with async/await

HttpService V3: the complete version with preferences pre-loading and JWT token support

This gist is a more complete version with the following features:

  • load a preferences.json file before each other request
  • send a TOKEN to the server with each request

Question? You can contact the author at info@salvatoreromeo.com

Enjoy writing better code!

Liked this content? Share on twitter…

Originally published at www.codeholiday.com.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.