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.
If you now click the “Set Data” button and refresh the application you should see this:
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:
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!