JavaScript Primer: Working with Promises

Promises are the JavaScript way of representing an item of work that will be performed asynchronously and that will be completed at some point in the future. The most common way to encounter promises is by making Ajax requests; the browser makes the HTTP request behind the scenes and uses a promise to notify your application when the request has completed. In Listing 5-42, I have created a minimal AngularJS application that makes an Ajax request.

Note This example relies on the todo.json file that I created at the start of the chapter.

Listing 5-42. Creating a Minimal AngularJS Application in the jsdemo.html File

<!DOCTYPE html>

<html ng-app=”demo”>

<head>

<title>Example</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script type=”text/javascript”>

var myApp = angular.module(“demo”, []);

myApp.controller(“demoCtrl”, function ($scope, $http)

{

var promise = $http.get(“todo.json”);

promise.success(function (data)

{

$scope.todos = data;

});

});

</script>

</head>

<body ng-controller=”demoCtrl”>

<div class=”panel”>

<h1>To Do</h1>

<table class=”table”>

<tr><td>Action</td><td>Done</td></tr>

<tr ng-repeat=”item in todos”>

<td>{{item.action}}</td>

<td>{{item.done}}</td>

</tr>

</table>

</div>

</body>

</html>

The AngularJS features that I have used in this listing will be familiar from Chapter 2. I have created am AngularJS module and given it a controller called demoCtrl. The controller uses the $scope object to provide data to a view that populates a table using data bindings and the ng-repeat directive. You can see how the browser displays this example in Figure 5-3.

The module, controller, and view are all AngularJS plumbing that I need to set up to show you how promises work. The key part of the listing is here:

var promise = $http.get(“todo.json”);

promise.success(function (data) {

$scope.todos = data;

});

The $http service (which I describe in Chapter 20) is used for making Ajax requests, and the get method takes the URL of the file that you want to retrieve from the server. (By just specifying the file name, I am telling the browser that the file I want is located alongside the currently displayed HTML document.)

The Ajax request is performed asynchronously, and the browser continues to run my simple application while the request is being made. The $http.get method returns a promise object that I can use to receive notifications about the Ajax request. In this example, I used the success method to register a callback function that will be invoked when the request has been completed. The callback function receives the data retrieved from the server, which I use to assign a property to the $scope, and this, in turn, gives the ng-repeat directive the content to populate the table with to-do items. The success method is one of three that promise objects define, as described in Table 5-7.

All three methods take functions as arguments and invoke them based on the outcome of the promise. The success callback function is passed the data retrieved from the server, and the error callback receives details of the problem that was encountered.

Tip Another way to think about the methods defined by a promise is that they are like events. In the same way that a callback function can be invoked when a user clicks a button and triggers an event, a promise will invoke a callback function when work has been completed.

All three promise methods return other promise objects, allowing asynchronous tasks to be chained together in sequence. Listing 5-43 contains a simple example.

Listing 5-43. Chaining Promises in the jsdemo.html File

<!DOCTYPE html>

<html ng-app=”demo”>

<head>

<title>Example</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script type=”text/javascript”>

var myApp = angular.module(“demo”, []);

myApp.controller(“demoCtrl”, function ($scope, $http) {

$http.get(“todo.json”).then(function (response) {

$scope.todos = response.data;

}, function () {

$scope.todos = [{action: “Error”}];

}).then(function () {

$scope.todos.push({action: “Request Complete”});

});

});

</script>

</head>

<body ng-controller=”demoCtrl”>

<div class=”panel”>

<h1>To Do</h1>

<table class=”table”>

<tr><td>Action</td><td>Done</td></tr>

<tr ng-repeat=”item in todos”>

<td>{{item.action}}</td>

<td>{{item.done}}</td>

</tr>

</table>

</div>

</body>

</html>

Here I have used the then method twice, the first time to handle the response from the call to the $http.get method and again to register a function that will be invoked afterward. It can be hard to read this kind of code, so I’ll use highlighting to show the sequence. First, I call the get method to create the Ajax request:

$http.get(“todo.json”).then(function (response) {

$scope.todos = response.data;

}, function () {

$scope.todos = [{action: “Error”}];

}).then(function () {

$scope.todos.push({action: “Request Complete”});

});

I used the then method to provide functions that will be called when the Ajax request completes. The first function is called when the request succeeds and the second when the request fails:

$http.get(“todo.json”).then(function (response) {

$scope.todos = response.data;

}, function () {

$scope.todos = [{action: “Error”}];

}).then(function () {

$scope.todos.push({action: “Request Complete”});

});

The promise guarantees that one of these functions will be invoked but not until the Ajax request has completed or failed. I use the then method again to add a further function:

$http.get(“todo.json”).then(function (response) {

$scope.todos = response.data;

}, function () {

$scope.todos = [{action: “Error”}];

}).then(function () {

$scope.todos.push({action: “Request Complete”});

});

This time I have passed only one function to the then method, meaning that I don’t want a notification if there is a problem. This final function adds an item to the data model irrespective of the preceding function that was invoked. You can see the effect of a successful Ajax request in Figure 5-4.

Tip Don’t worry if chaining doesn’t make sense at the moment. You’ll quickly get the idea when you start to use promises in your own projects, and you’ll see more promise examples in Chapter 20 (when I describe AngularJS Ajax support) and in Chapter 21 (when I describe RESTful web services).

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *