I’ve been using promises in JavaScript for a while now and thought I knew almost everything about them. But yesterday a co-worker showed me how it’s possible to manipulate the resolved value from one promise to another when they are chained together. Let me show you what I mean.
Below is a chained promise that logs the name to the console. It’s pretty straight forward.
getName().then(function(name) {
console.log(name);
}).then(function(name) {
console.log(name);
});
> 'Paul'
> 'Paul'
Now let’s have the first then
function append a last name and return it. And now notice the output of the second log statement:
getName().then(function(name) {
console.log(name);
return name + ' Yoder';
}).then(function(name) {
console.log(name);
});
> 'Paul'
> 'Paul Yoder'
In the first example, both then
functions received the same resolved value from the original promise. But in the second example, the first then
function returned a different value, and the second then
function received that different value instead of the original value from the promise.
Applying Chained Promises to Angular
I was able to clean up a lot of my Angular code after finding out about this feature for chained promises. Here’s one example of how I was able to clean up the code.
Let’s say you have a REST API that returns information about a specific company. And the return value looks something like this:
{
companyName: 'ACME',
phone: '402-555-5555',
employeeCount: 100
}
And you have an Angular service that consumes the REST endpoint and returns the employee count for the company. And it looks something like this:
angular.module('app').factory('employeeCountService', function($http, $q) {
return {
getByCompanyId: function(companyId) {
var deferred = $q.deferred()
$http.get('/companies/' + companyId).then(function(companyInfo) {
deferred.resolve(companyInfo.employeeCount);
});
return deferred.promise;
}
};
});
// Here's a controller that uses the companyEmployeeCount service
angular.module('app').controller('HomeCtrl', function($scope, employeeCountService, companyId) {
employeeCountService.getByCompanyId(companyId).then(function(employeeCount) {
$scope.employeeCount = employeeCount;
});
});
Notice that $q.deferred
was used because we had to manipulate the returned value from the REST API and resolve the promise to only include the employee count.
By taking advantage of what we just learned about chained promises though, we can clean up the amount of code needed in the employeeCountService
service to the following:
angular.module('app').factory('employeeCountService', function($http) {
return {
getByCompanyId: function(companyId) {
$http.get('/companies/' + companyId).then(function(companyInfo) {
return companyInfo.employeeCount;
});
}
};
});
The controller code that uses employeeCountService
did not change, and the getByCompanyId
function no longer has to create its own deferred object. So there’s less code and it’s easier to understand, and I consider that a big win!