Using Async/Await Syntax for Promises in Ionic

Using Async/Await Syntax for Promises in Ionic

Follow Josh Morony on

When TypeScript 2.1 was released, support for Async/Await came with it. It is not really an entirely new concept, but it does allow you to deal with asynchronous situations that Promises create using a more succinct syntax. Since Ionic is now using TypeScript 2.2.1 at the time of writing this, we are able to make use of Async/Await in our Ionic applications.

In this tutorial, I am going to show you exactly how you can use Async/Await with promises in an Ionic application, and compare it to the “normal” syntax. In the summary section, I will also express my opinion on Async/Await vs. normal promise handling, and whether I think it should be the preferred option.

If you’re unfamiliar with the concept of asynchronous code I would recommend reading Dealing with Asynchronous Code in Ionic first.

UPDATE: I’ve released a newer video about async/await that you can check out here if you like: Async / Await Basics in JavaScript Explained.

Before We Get Started

Last updated for Ionic 3.0.1

Before you go through this tutorial, you should have at least a basic understanding of Ionic concepts. You must also already have Ionic set up on your machine.

If you’re not familiar with Ionic already, I’d recommend reading my Ionic Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic, then take a look at Building Mobile Apps with Ionic.

1. Generate a New Ionic Application

Let’s start off by generating a new blank Ionic application with the following command:

ionic start ionic-async-await blank --v2

Once that has finished generating, you should make it your working directory by running the following command:

cd ionic-async-await

We won’t need to set up any additional components or providers for this demonstration, but we will need to set up the Ionic Storage Module. Fetching data from storage is asynchronous, so we will be using that as an example. To set up storage, we will need to make a change to the app.module.ts file.

Modify src/app/app.module.ts to reflect the following:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { IonicStorageModule } from '@ionic/storage';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

2. Fetching Data from Storage

We are going to implement a simple page now that will allow us to set some data in storage. We will store a value for apple and banana but we will retrieve the data for those items differently. We will use the standard syntax for apple but we will use the new async/await syntax for banana.

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Storage } from '@ionic/storage';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    apple: any;
    banana: any;

    constructor(public navCtrl: NavController, public storage: Storage) {

    }

    ionViewDidLoad(){

        this.getFromStorageStandard().then((result) => {
            this.apple = result;
        });

        this.banana = this.getFromStorageAsync();

    }

    setInStorage(){
        this.storage.set('apple', '3.99');
        this.storage.set('banana', '4.50');
    }

    getFromStorageStandard(){

        return this.storage.get('apple');

    }

    async getFromStorageAsync(){

        return await this.storage.get('banana');

    }

}

We have a setInStorage function that we will just trigger with a button click later to intially set up these values in storage. We then have two separate functions getFromStorageStandard and getFromStorageAsync that are both called from ionViewDidLoad and handle setting up the apple and banana member variables.

The getFromStorageStandard function just returns the result of the get method, which is a promise, and then we just handle that promise as we usually would in the ionViewDidLoad function – we set up a handler, and then inside that handler, we deal with the result.

The getFromStorageAsync function using the async/await syntax which you should immediately notice. We add async before the function definition, and we add await before the function call that returns a promise. It doesn’t immediately look all that different, but the way in which we handle the result in ionViewDidLoad is very different. Instead of having to add a handler to a returned promise in order to extract the result, we just assign the result directly to this.banana. Although the end result is pretty much the same, this async/await syntax lets you write asynchronous code in a more synchronous manner.

Let’s implement the template for this now so that we can check out the values that get set for apple and banana.

Modify src/pages/home/home.html to reflect the following:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>

    <h3>Apple: {{apple}}</h3>
    <h3>Banana: {{banana}}</h3>

    <button ion-button (click)="setInStorage()">Set Data</button>

</ion-content>

We have our button here that we will be able to click to set up the data in storage. Before setting the data you will see that whilst the value for {{apple}} is undefined, the value for {{banana}} is actually an object.

Async/Await Example

If you now click the “Set Data” button and refresh the application you should see this:

Async/Await Example

Even though we have some data set in storage now, and the normal Promise syntax is successfully displaying the apple’s price, the banana’s price is still displaying as [object Object]. That’s because this.banana does not directly contain the value that we are attempting to display, it contains a special object for handling this async/await behaviour (which is why it was not undefined even though we hadn’t set any data yet). In order to display the value correctly, we need to use the async pipe.

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>

    <h3>Apple: {{apple}}</h3>
    <h3>Banana: {{banana | async}}</h3>

    <button ion-button (click)="setInStorage()">Set Data</button>

</ion-content>

Now rather than just attempting to interpolate the async/await object directly, it should display the value correctly:

Async/Await Example

If you are unfamiliar with the use of pipes in Ionic, I would recommend watching Custom Pipes in Ionic. A pipe just basically takes data and transforms it in some way before displaying it in the template.

Summary

I can see the attractiveness of using async and await, it’s simple and gets the job done. However, I am still undecided on whether I prefer the syntax, especially in terms of readability and simplicity for beginners. Take the following if statement for example:

if(someNumber > 40){
    this.bigNumber = true;
} else {
    this.bigNumber = false;
}

This could also be written more succinctly using the ternary operator as:

someNumber > 40 ? this.bigNumber = true : this.bigNumber = false;

but I don’t think that necessarily makes it the better option, it’s certainly a lot less readable than the first option. Although I do use it sometimes, I generally avoid using the ternary operator and I feel like I will probably do the same for async/await. If I just look at this section of the code:

this.banana = this.getFromStorageAsync();

It may not be immediately obvious that we are dealing with asynchronous data here, but if we do this:

this.getFromStorageStandard().then((result) => {
    this.apple = result;
});

It is very clear that we are working with asynchronous data. The other thing that I think may be an issue is that since we can’t use async/await with Observables (unless that observable is converted to a promise, which is quite easy to do) we would end up having one syntax for handling results from promises and one syntax for handling results from observables. I think this could also be confusing.

These are just my surface level thoughts, though, I haven’t spent a lot of time working with async/await yet. If you have an opinion on which syntax you prefer, please leave a comment!

Check out my latest videos: