← all articles

Using Promises in AngularJS

Tamas Piros | 4 February 2016

Callbacks

In this example we are not using promises at all. What we are trying to achieve is the following: Get information about a destination airport via a webservice call, once that’s done, we’d like to retrieve weather inforamtion for the same destination.

The problem is that we have to nest these calls. And if we’d like to extract information out of the returned dataset that just adds complexity to the app.

Pyramid of doom

You can see in the below code snippet how the requests are nested. Imagine if we'd have 3 or 4 requests, each time would have to further nest our code.

'use strict';
var city    = '';
var country = '';
var weather = '';

function go() {

 var code = document.getElementById('iata').value;
 var request = new XMLHttpRequest();
 request.open('GET', 'http://airportapi-tpiros1.rhcloud.com/airport/IATA/' + code);
 request.onload = function() {
   if (request.status === 200) {
     // parse & build up return values
     var result = JSON.parse(request.response)[0];
     city = result.municipality;
     country = result.iso_country;
     // we are resuing the values to make our second web service call
     request.open('GET', 'http://api.openweathermap.org/data/2.5/weather?q=' + city + ',' + country);
     request.onload = function() {
       if (request.status === 200) {
         // again, parse the values
         var result = JSON.parse(request.response);
         var celsius = (result.main.temp - 273.15).toPrecision(2); //Kelvin to Celsius
         var description = result.weather[0].description;
         weather = celsius + '°C and ' + description;
         console.log('The weather in ' + city + ', ' + country + ' is ' + weather + '.');
       } else {
         console.log(request.statusText);
       }
     }
     // this sends the second API call
     request.send();
   } else {
     console.log(request.statusText);
   }
 }
 // this sends the first API call
 request.send();
 // when this line is reached, we don't have the values fulfilled ... so we need to move it up
 // console.log('The weather in ' + city + ', ' + country + ' is ' + weather + '.');
}

Use the above script in conjunction with this HTML:

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title></title>
 <style>
   html, input {
     font-family: Georgia;
     font-size: 32px;
     font-weight: 700;
   }
   input[type=text] {
     color: #aaa;
   }
   .btn {
     color: #fff;
     border: solid 1px #ccc;
     border-radius: 16px;
     padding: 5px;
     background: -webkit-gradient(linear, left top, left bottom, from(#00adee), to(#0078a5));
     background: -moz-linear-gradient(top, #00adee, #0078a5);
   }
 </style>
 <script>
   function go() {
     console.log('%c You should not see this message. Plese uncomment a script line in the HTML source.', 'color: red;');
   }
 </script>
 <script src="app.promise.js"></script>
 <!-- <script src="app.nopromise.js"></script> -->
</head>
<body>
<div>
 <p>IATA code: <input type="text" maxlength="3" size="3" id="iata"></p>
 <p><input type="submit" class="btn" value="Get Information" onclick="go()"></p>
</div>
</body>
</html>

The above HTML code allows you to enter a three letter airport code (IATA code) and clicking the button will initiate the request to the web service specified in the previous JavaScript code snippet.

Using promises

Let's take a look at how can we rewrite our JavaScript code with promises so that we can get a chain of events happening:

// Native promise implementation
'use strict';
var city    = '';
var country = '';

// this function returns a promise (resolves or rejects)
function getPromise(url) {
 var promise = new Promise(function(resolve, reject) {
   var request = new XMLHttpRequest();
   request.open('GET', url);
   request.onload = function() {
     if (request.status === 200) {
       resolve(request.response)
     } else {
       reject(new Error(request.statusText));
     }
   };

   request.onerror = function() {
     reject(new Error('Cannot get data'));
   };

   request.send();
 });

 return promise;
}

function go() {
 var code = document.getElementById('iata').value;
 var promise = new getPromise('http://airportapi-tpiros1.rhcloud.com/airport/IATA/' + code);
 promise
   .then(function(result) {
     result = JSON.parse(result)[0];
     city = result.municipality;
     country = result.iso_country;
     return getPromise('http://api.openweathermap.org/data/2.5/weather?q=' + city + ',' + country);
   })
   .then(function(result) {
     result = JSON.parse(result);
     var celsius = (result.main.temp - 273.15).toPrecision(2); //Kelvin to Celsius
     var description = result.weather[0].description;
     var weather = celsius + '°C and ' + description;
     console.log('The weather in ' + city + ', ' + country + ' is ' + weather + '.');
   });
}

Notice how we have a the go() function returning a promise. Promises can be chained using the then() method. This method also returns a promise and it takes two function arguments - a function that gets invoked when the promise is fulfilled and another one when the promise is rejected (not shown in the example).

In AngularJS some service do implement Promises natively - one such service is $http. So all HTTP calls ($http.get(), $http.post() and so on all return a promise therefore you can call the .then() methods on them.

In light of this, the previous airport example could be rewritten using the following call:

$http
   .get('http://airportapi-tpiros1.rhcloud.com/airport/IATA/' + vm.code)
   .then(function(airportInfo) {
     console.log(airportInfo[0].name + ' is an airport at ' + airportInfo[0].municipality
     + ', ' + airportInfo[0].iso_country);
   });

In a future article we'll see how you can also make use of Promises in AngularJS via the resolve property on Angular routes.

Free email mini-course on
Full Stack development

Sign up now to receive a free email mini-course covering the MEAN stack and more. Also be the first to know when we release new courses and videos.