Using `$q.defer()` in AngularJS? Try the $q 'constructor' instead.

Emil

Although I’m a great fan of using the ($q) Promise API in AngularJS, I never really liked using $q.defer() and its Deferred API. I always found using var deferred = $q.defer() together with $.resolve(..) and $.reject(..) to be too verbose.

After recently checking the $q service documentation I stumbled upon the $q constructor that results in way less verbose code. To illustrate the usage of the $q constructor I will create a function that wraps the result of a Geolocation#getCurrentPosition invocation as an AngularJS $q promise. Using the traditional $q.defer() approach the function wrapper will look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/\*\* @return {Promise} \*/
function getGeoLocation() {
var deferred = $q.defer();

$window.navigator.geolocation.getCurrentPosition(
function(position) { // success callback
return deferred.resolve(position);
},
function(positionError) { // error callback
return deferred.reject(positionError);
});

return deferred.promise;
}

When using the $q constructor, I could rewrite the code as follows, resulting in less verbose code:

1
2
3
4
5
6
7
8
9
10
11
12
13
/\*\* @return {Promise} \*/
function getGeoLocation() {
return $q(function(resolve, reject) {
$window.navigator.geolocation.getCurrentPosition(
function(position) { // success callback
resolve(position);
},
function(positionError) { // error callback
reject(positionError);
}
});
});
}

And since in this case we are merely passing through the arguments of the success and error callbacks, I could rewrite the code to be even smaller:

1
2
3
4
5
6
/\*\* @return {Promise} \*/
function getGeoLocation() {
return $q(function(resolve, reject) {
$window.navigator.geolocation.getCurrentPosition(resolve, reject);
});
}

As you can see, using the $q constructor produces code with less boilerplate, making it more readable in the process. Notice that although it’s being called the “$q constructor” we are not actually doing a new on $q. Possibly, the AngularJS team calls it this way because it mimics the ECMAScript 2015 Promise constructor API. Next to the $q constructor there are some other alternatives to using the verbose $q.defer(). In most cases you can probably use a less verbose alternative as is nicely described in this blog post that I stumbled upon while doing some additional research for my own blog post.

Unit testing an AngularJS directive's private functions. Grasping AngularJS 1.5 directive bindings by learning from Angular 2