Dave Smith
@djsmith42
http://thesmithfam.org/blog
deferred.resolve(punchline);
function getCurrentUser(callback) { $http.get("/api/users/me") .success(function(user) { callback(user); }); }
getCurrentUser(function(user) { // Do something with user });
function getPermissions(callback) { $http.get("/api/permissions") .success(function(permissions) { callback(permissions); }); }
getCurrentUser(function(user) { // Do something with user }); getPermissions(function(permissions) { // Do something with permissions }); // Do something with user and permissions... // How???
getCurrentUser(function(user) { getPermissions(function(permissions) { // Do something with permissions
// Do something with user
});
});
getCurrentUser(function(user) { getPermissions(function(permissions) {
getFoo(function(foo) {
getBar(function(bar) {
// :(
});
});
});
});
With $q, we can do better!
But how do I use $q?
Two simple steps:
function getCurrentUser() { var deferred = $q.defer(); $http.get("/api/users/me") .success(function(user) { deferred.resolve(user); }); return deferred.promise; }
getCurrentUser().then(function(user) { // Do something with user });
Looks similar, but it's very different.
Suddenly, all your async operations become:
$q.all([getCurrentUser(), getPermissions()]) .then(function(responses) { var user = responses[0]; var permissions = responses[1]; });
By the way, this can be even nicer...
My favorite spread
function spread(func) { return function(array) { return func.apply(void 0, array); } }
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
https://github.com/kriskowal/q/blob/v1/q.js
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
$q.all([getCurrentUser(), getPermissions()]) .then(spread(function(user, permissions) { // Do something with user and permissions }));
Almost as delicious as
var promise1 = foo(); var promise2 = promise1.then(function() {...}); var promise3 = promise2.then(function() {...}); // By the way: expect(promise1 === promise2).toBe(false);
expect(promise2 === promise3).toBe(false);
var promises = [getCurrentUser()]; if (shouldGetPermissions) { promises.push(getPermissions()); } $q.all(promises).then(function() { // all done! });
deferred.resolve() promise.then()
deferred.reject()
promise.then(function() { // success }, function() { // failure
});
The notifier can send errors like this:
The receiver can receive them like this:
deferred.notify(...)
promise.then(function() { // success }, function() { // failure }, function() { // progress (called 0 or more times) });
The notifier can send progress updates like this:
The receiver can receive them like this:
Once you resolve or reject a deferred object, it cannot be resolved or deferred again.
You can connect a .then() to a promise after its deferred has been resolved, and your callback will be called at the next digest loop.
$q.when()
$q.when()
"Everything's better wrapped in a promise..."
"...even promises"
myApp.factory("movies", function($q, $http) {
var cachedMovies;
return {
getMovies: function() {
return $q.when(cachedMovies || helper());
}
};
function helper() {
var deferred = $q.defer();
$http.get("/movies").success(function(movies) {
cachedMovies = movies;
deferred.resolve(movies);
});
return deferred.promise;
}
});
myApp.factory("movies", function($q, $http) {
var cachedMovies, p;
return {
getMovies: function() {
return $q.when(cachedMovies || p || helper());
}
};
function helper() {
var deferred = $q.defer();
p = deferred.promise;
$http.get("/movies").success(function(movies) {
cachedMovies = movies;
deferred.resolve(movies);
});
return deferred.promise;
}
});
$http already does caching.
Look at the "cache" argument.
https://docs.angularjs.org/api/ng/service/$http
Promises wrapped in
promises wrapped in
promise wrapped in
...
Promises are Composable
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) {
resolvedValue = value;
});
deferred.resolve(123);
expect(resolvedValue).toBeUndefined();
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));
<-- What?
<-- Oh! Ok.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://promisesaplus.com/implementations
function doSomething(callback) { ... callback(); } function doSomething() { ... return promise; }