An Introduction to Mastering JavaScript Promises and Its Implementation in Angular.js

In this article by Muzzamil Hussain, the author of the book Mastering JavaScript Promises, introduces us to promises in JavaScript and its implementation in Angular.js.

(For more resources related to this topic, see here.)

For many of us who are working with JavaScript, we all know that working with JavaScript means you must have to be a master is asynchronous coding but this skill doesn't come easily. You have to understand callbacks and when you learn it, a sense of realization started to bother you that managing callbacks is not a very easy task, and it's really not an effective way of asynchronous programming.

Those of you who already been through this experience, promises is not that new; even if you haven't used it in your recent project, but you would really want to go for it. For those of you who neither use any of callbacks or promises, understanding promises or seeking difference between callbacks and promise would be a hard task.

Some of you have used promises in JavaScript during the use of popular and mature JavaScript libraries such as Node.js, jQuery, or WinRT. You are already aware of the advantages of promises and how it's helping out in making your work efficient and code look beautiful.

For all these three classes of professionals, gathering information on promises and its implementation in different libraries is quite a task and much of the time you spent is on collecting the right information about how you can attach an error handler in promise, what is a deferred object, and how it can pass it on to different function. Possession of right information in the time you need is the best virtue one could ask for.

Keeping all these elements in mind, we have written a book named Mastering JavaScript Promises. This book is all about JavaScript and how promises are implemented in some of the most renowned libraries of the world. This book will provide a foundation for JavaScript, and gradually, it will take you through the fruitful journey of learning promises in JavaScript. The composition of chapters in this book are engineered in such a way that it provides knowledge from the novice level to an advance level. The book covers a wide range of topics with both theoretical and practical content in place. You will learn about evolution of JavaScript, the programming models of different kinds, the asynchronous model, and how JavaScript uses it. The book will take you right into the implementation mode with a whole lot of chapters based on promises implementation of WinRT, Node.js, Angular.js, and jQuery. With easy-to-follow example code and simple language, you will absorb a huge amount information on this topic.

Needless to say, books on such topics are in itself an evolutionary process, so your suggestions are more than welcome. Here are few extracts from the book to give you a glimpse of what we have in store for you in this book, but most of the part in this section will focus on Angular.js and how promises are implemented in it.

Let's start our journey to this article with programming models.

Models

Models are basically templates upon which the logics are designed and fabricated within a compiler/interpreter of a programming language so that software engineers can use these logics in writing their software logically. Every programming language we use is designed on a particular programming model. Since software engineers are asked to solve a particular problem or to automate any particular service, they adopt programming languages as per the need.

There is no set rule that assigns a particular language to create products. Engineers adopt any language based on the need.

The asynchronous programming model

Within the asynchronous programming model, tasks are interleaved with one another in a single thread of control.

This single thread may have multiple embedded threads and each thread may contain several tasks linked up one after another. This model is simpler in comparison to the threaded case, as the programmers always know the priority of the task executing at a given slot of time in memory.

Consider a task in which an OS (or an application within OS) uses some sort of a scenario to decide how much time is to be allotted to a task, before giving the same chance to others. The behavior of the OS of taking control from one task and passing it on to another task is called preempting.

Promise

The beauty of working with JavaScript's asynchronous events is that the program continues its execution, even when it doesn't have any value it needs to work that is in progress. Such scenarios are named as yet known values from unfinished work. This can make working with asynchronous events in JavaScript challenging.

Promises are a programming construct that represents a value that is still unknown. Promises in JavaScript enable us to write asynchronous code in a parallel manner to synchronous code.

How to implement promises

So far, we have learned the concept of promise, its basic ingredients, and some of the basic functions it has to offer in nearly all of its implementations, but how are these implementations using it? Well, it's quite simple. Every implementation, either in the language or in the form of a library, maps the basic concept of promises. It then maps it to a compiler/interpreter or in code. This allows the written code or functions to behave in the paradigm of promise, which ultimately presents its implementations.

Promises are now part of the standard package for many languages. The obvious thing is that they have implemented it in their own way as per the need.

Implementing promises in Angular.js

Promise is all about how async behavior can be applied on a certain part of an application or on the whole. There is a list of many other JavaScript libraries where the concept of promises exists but in Angular.js, it's present in a much more efficient way than any other client-side applications.

Promises comes in two flavors in Angular.js, one is $q and the other is Q. What is the difference between them? We will explore it in detail in the following sections. For now, we will look at what promise means to Angular.js.

There are many possible ways to implement promises in Angular.js. The most common one is to use the $q parameter, which is inspired by Chris Kowal's Q library. Mainly, Angular.js uses this to provide asynchronous methods' implementations.

With Angular.js, the sequence of services is top to bottom starting with $q, which is considered as the top class; within it, many other subclasses are embedded, for example, $q.reject() or $q.resolve(). Everything that is related to promises in Angular.js must follow the $q parameters.

Starting with the $q.when() method, it seems like it creates a method immediately rather it only normalizes the value that may or may not create the promise object. The usage of $q.when() is based on the value supplied to it. If the value provided is a promise, $q.when() will do its job and if it's not, a promise value, $q.when() will create it.

The schematics of using promises in Angular.js

Since Chris Kowal's Q library is the global provider and inspiration of promises callback returns, Angular.js also uses it for its promise implementations. Many of Angular.js services are by nature promise oriented in return type by default. This includes $interval, $http, and $timeout. However, there is a proper mechanism of using promises in Angular.js. Look at the following code and see how promises maps itself within Angular.js:

var promise = AngularjsBackground();

promise.then(
function(response) {
   // promise process
},
function(error) {
   // error reporting
},
function(progress) {
   // send progress
  
});

All of the mentioned services in Angular.js return a single object of promise. They might be different in taking parameters in, but in return all of them respond back in a single promise object with multiple keys. For example, $http.get returns a single object when you supply four parameters named data, status, header, and config.

$http.get('/api/tv/serials/sherlockHolmes ')
.success(function(data, status, headers, config) {
   $scope.movieContent = data;
});

If we employ the promises concept here, the same code will be rewritten as:

var promise = $http.get('/api/tv/serials/sherlockHolmes ')
promise.then(
function(payload) {
   $scope.serialContent = payload.data;
});

The preceding code is more concise and easier to maintain than the one before this, which makes the usage of Angular.js more adaptable to the engineers using it.

Promise as a handle for callback

The implementation of promise in Angular.js defines your use of promise as a callback handle. The implementations not only define how to use promise for Angular.js, but also what steps one should take to make the services as "promise-return". This states that you do something asynchronously, and once your said job is completed, you have to trigger the then() service to either conclude your task or to pass it to another then() method: /asynchronous _task.then().then().done().

In simpler form, you can do this to achieve the concept of promise as a handle for call backs:

angular.module('TVSerialApp', [])
.controller('GetSerialsCtrl',
   function($log, $scope, TeleService) {
     $scope.getserialListing = function(serial) {
       var promise =
         TeleService.getserial('SherlockHolmes');
       promise.then(
         function(payload) {
           $scope.listingData = payload.data;
         },
         function(errorPayload) {
           $log.error('failure loading serial', errorPayload);
       });
     };
})
.factory('TeleService', function($http) {
   return {
     getserial: function(id) {
       return $http.get(''/api/tv/serials/sherlockHolmes' + id);
     }
   }
});

Blindly passing arguments and nested promises

Whatever service of promise you use, you must be very sure of what you are passing and how this can affect the overall working of your promise function. Blindly passing arguments can cause confusion for the controller as it has to deal with its own results too while handling other requests. Say we are dealing with the $http.get service and you blindly pass too much of load to it. Since it has to deal with its own results too in parallel, it might get confused, which may result in callback hell. However, if you want to post-process the result instead, you have to deal with an additional parameter called $http.error. In this way, the controller doesn't have to deal with its own result, and calls such as 404 and redirects will be saved.

You can also redo the preceding scenario by building your own promise and bringing back the result of your choice with the payload that you want with the following code:

factory('TVSerialApp', function($http, $log, $q) {
return {
   getSerial: function(serial) {
     var deferred = $q.defer();
     $http.get('/api/tv/serials/sherlockHolmes' + serial)
       .success(function(data) {
         deferred.resolve({
           title: data.title,
           cost: data.price});
       }).error(function(msg, code) {
           deferred.reject(msg);
           $log.error(msg, code);
       });
       return deferred.promise;
   }
}
});

By building a custom promise, you have many advents. You can control inputs and output calls, log the error messages, transform the inputs into desired outputs, and share the status by using the deferred.notify(mesg) method.

Deferred objects or composed promises

Since custom promise in Angular.js can be hard to handle sometimes and can fall into malfunction in the worse case, the promise provides another way to implement itself. It asks you to transform your response within a then method and returns a transformed result to the calling method in an autonomous way. Considering the same code we used in the previous section:

this.getSerial = function(serial) {
   return $http.get('/api/tv/serials/sherlockHolmes'+ serial)
       .then(
               function (response) {
                   return {
                       title: response.data.title,
                       cost: response.data.price
 
                   });
                 });
};

The output we yield from the preceding method will be a chained, promised, and transformed. You can again reuse the output for another output, chain it to another promise, or simply display the result.

The controller can then be transformed into the following lines of code:

$scope.getSerial = function(serial) {
service.getSerial(serial)
.then(function(serialData) {
   $scope.serialData = serialData;
});
};

This has significantly reduced the lines of code. Also, this helps us in maintaining the service level since the automechanism of failsafe in then() will help it to be transformed into failed promise and will keep the rest of the code intact.

Dealing with the nested calls

While using internal return values in the success function, promise code can sense that you are missing one most obvious thing: the error controller. The missing error can cause your code to stand still or get into a catastrophe from which it might not recover. If you want to overcome this, simply throw the errors. How? See the following code:

this.getserial = function(serial) {
   return $http.get('/api/tv/serials/sherlockHolmes' + serial)
       .then(
           function (response) {
               return {
                   title: response.data.title,
                   cost: response.data.price
              });
           },
           function (httpError) {
               // translate the error
               throw httpError.status + " : " +
                   httpError.data;
           });
};

Now, whenever the code enters into an error-like situation, it will return a single string, not a bunch of $http statutes or config details. This can also save your entire code from going into a standstill mode and help you in debugging. Also, if you attached log services, you can pinpoint the location that causes the error.

Concurrency in Angular.js

We all want to achieve maximum output at a single slot of time by asking multiple services to invoke and get results from them. Angular.js provides this functionality via its $q.all service; you can invoke many services at a time and if you want to join all/any of them, you just need then() to get them together in the sequence you want.

Let's get the payload of the array first:

[
{ url: 'myUr1.html' },
{ url: 'myUr2.html' },
{ url: 'myUr3.html' }
]

And now this array will be used by the following code:

service('asyncService', function($http, $q) {
     return {
       getDataFrmUrls: function(urls) {
         var deferred = $q.defer();
         var collectCalls = [];
         angular.forEach(urls, function(url) {
           collectCalls.push($http.get(url.url));
         });
 
         $q.all(collectCalls)
         .then(
           function(results) {
           deferred.resolve(
             JSON.stringify(results))
         },
         function(errors) {
         deferred.reject(errors);
         },
         function(updates) {
           deferred.update(updates);
         });
         return deferred.promise;
       }
     };
});

A promise is created by executing $http.get for each URL and is added to an array. The $q.all function takes the input of an array of promises, which will then process all results into a single promise containing an object with each answer. This will get converted in JSON and passed on to the caller function.

The result might be like this:

[
promiseOneResultPayload,
promiseTwoResultPayload,
promiseThreeResultPayload
]

The combination of success and error

The $http returns a promise; you can define its success or error depending on this promise. Many think that these functions are a standard part of promise—but in reality, they are not as they seem to be.

Using promise means you are calling then(). It takes two parameters—a callback function for success and a callback function for failure.

Imagine this code:

$http.get("/api/tv/serials/sherlockHolmes")
.success(function(name) {
   console.log("The tele serial name is : " + name);
})
.error(function(response, status) {
   console.log("Request failed " + response + " status code: " +     status);
};

This can be rewritten as:

$http.get("/api/tv/serials/sherlockHolmes")
.success(function(name) {
   console.log("The tele serial name is : " + name);
})
.error(function(response, status) {
   console.log("Request failed " + response + " status code: " +     status);
};
 
$http.get("/api/tv/serials/sherlockHolmes")
.then(function(response) {
   console.log("The tele serial name is :" + response.data);
}, function(result) {
   console.log("Request failed : " + result);
};

One can use either the success or error function depending on the choice of a situation, but there is a benefit in using $http—it's convenient. The error function provides response and status, and the success function provides the response data.

This is not considered as a standard part of a promise. Anyone can add their own versions of these functions to promises, as shown in the following code:

//my own created promise of success function
 
promise.success = function(fn) {
   promise.then(function(res) {
       fn(res.data, res.status, res.headers, config);
   });
   return promise;
};
 
//my own created promise of error function
 
promise.error = function(fn) {  
   promise.then(null, function(res) {
       fn(res.data, res.status, res.headers, config);
   });
   return promise;
};

The safe approach

So the real matter of discussion is what to use with $http? Success or error? Keep in mind that there is no standard way of writing promise; we have to look at many possibilities.

If you change your code so that your promise is not returned from $http, when we load data from a cache, your code will break if you expect success or error to be there.

So, the best way is to use then whenever possible. This will not only generalize the overall approach of writing promise, but also reduce the prediction element from your code.

Route your promise

Angular.js has the best feature to route your promise. This feature is helpful when you are dealing with more than one promise at a time. Here is how you can achieve routing through the following code:

$routeProvider
.when('/api/', {
     templateUrl: 'index.php',
     controller: 'IndexController'
})
.when('/video/', {
     templateUrl: 'movies.php',
     controller: 'moviesController'
})

As you can observe, we have two routes: the api route takes us to the index page, with IndexController, and the video route takes us to the movie's page.

app.controller('moviesController', function($scope, MovieService) {
   $scope.name = null;
 
   MovieService.getName().then(function(name) {
       $scope.name = name;
   });
});

There is a problem, until the MovieService class gets the name from the backend, the name is null. This means if our view binds to the name, first it's empty, then its set.

This is where router comes in. Router resolves the problem of setting the name as null. Here's how we can do it:

var getName = function(MovieService) {
       return MovieService.getName();
   };
 
$routeProvider
.when('/api/', {
     templateUrl: 'index.php',
     controller: 'IndexController'
})
.when('/video/', {
     templateUrl: 'movies.php',
     controller: 'moviesController'
})

After adding the resolve, we can revisit our code for a controller:

app.controller('MovieController', function($scope, getName) {
 
   $scope.name = name;
 
});

You can also define multiple resolves for the route of your promises to get the best possible output:

$routeProvider
.when('/video', {
     templateUrl: '/MovieService.php',
     controller: 'MovieServiceController',
     // adding one resole here
     resolve: {
         name: getName,
         MovieService: getMovieService,
         anythingElse: getSomeThing
     }
     // adding another resole here
       resolve: {
         name: getName,
         MovieService: getMovieService,
         someThing: getMoreSomeThing
     }
})

An introduction to WinRT

Our first lookout for the technology is WinRT. What is WinRT? It is the short form for Windows Runtime. This is a platform provided by Microsoft to build applications for Windows 8+ operating system. It supports application development in C++/ICX, C# (C sharp), VB.NET, TypeScript, and JavaScript.

Microsoft adopted JavaScript as one of its prime and first-class tools to develop cross-browser apps and for the development on other related devices. We are now fully aware of what the pros and cons of using JavaScript are, which has brought us here to implement the use of promise.

Summary

This article/post is just to give an understanding of what we have in our book for you; focusing on just Angular.js doesn't mean we have only one technology covered in the entire book for implementation of promise, it's just to give you an idea about how the flow of information goes from simple to advanced level, and how easy it is to keep on following the context of chapters.

Within this book, we have also learned about Node.js, jQuery, and WinRT so that even readers from different experience levels can read, understand, and learn quickly and become an expert in promises.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Mastering JavaScript Promises

Explore Title