Angular.JS, I Promise!
Synchronous Approach
Consider the following Pseudo Code:
fileName = prompt('Please enter a file name');
if (fileName) {
data = loadUrl('http://server/pictureService');
fs = requestFileSystem(…);
file = fs.getFile(fileName);
file.write(data);
alert('Your changes were saved!');
}
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Asynchronous Version
This is what it would look like in the async world of JS:
showPrompt("Please enter a file name", function(fileName) {
loadData("http://www.server/pictureService",
function(data) {
requestFileSystem(…, function(fs) {
fs.getFile(fileName, function(file) {
file.write(data, function() {
showAlert("You changes were saved!");
});
});
});
});
});
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
• Nesting makes the Async version hard to read –
Pyramid Code
• We could split into many functions, but the control flow
would be hard to follow
• The Async version doesn’t even have any error handling
code and yet it is so messy
• Would you enjoy reading and maintaining code like this?
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Definition
A promise represents a future value, usually a future result
of an asynchronous operation, and allows us to define what
will happen once this value becomes available, or when an
error occurs.
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Basic Usage Pattern
var promise = asyncFunction(parameters);
promise.then(
function (result) {
// Do Something with the result
},
function (error) {
// Handle error (exception, etc).
});
Success Handler
Error Handler
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
AngularJS 1.2 Extensions
var promise = asyncFunction(parameters);
promise.then(
function (result) {
// Do Something with the result
},
function (error) {
// Handle error (exception, etc).
},
function (update) {
// Receive progress updates (e.g.
// amount of data downloaded so far)
});
Success Handler
Error Handler
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Progress Handler
Angular.JS, I Promise!
Catch and Finally
var promise = asyncFunction(parameters);
promise.catch(function (error) {
// Syntax sugar for defining an error handler
});
promise.finally(function() {
// Will be executed regardless of success/failure.
});
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
The Power of Promises
• You can chain them to create code flows
• Error propagates, so you can catch it on the end of the
chain
• Actually, they are much like an asynchronous equivalent
for the well-known try-catch-finally clause.
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Chaining Promises
var finalResult = getCurrentUserId()
.then(function (userId) {
if (!userId) { throw "Invalid User"; }
return loadUserData(userId);
}).then(function(userData) {
if (!userData) { return "default.png"; };
return prepareUserThumbnail(userData.profilePic);
}).then(function(thumbnail) {
console.log("User thumbnail: " + thumbnail);
return thumbnail;
}, function(error) {
// Handle error (exception, etc).
});
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
$HTTP Returns a Promise
function promiseCtrl($scope, $http) {
$http.get("/README.txt").then(function(result) {
$scope.readme = result.data;
});
}
<div ng-controller="promiseCtrl">
<h2>HTTP Request Demo</h2>
<pre>{{readme || "Loading…"}}</pre>
<b ng-show="readme">Request finished.</b>
</div>
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Promise Chaining
function promiseCtrl($scope, $http) {
$http.get("/userInfo.json").then(function(result) {
var user = $result.data;
$scope.user = user;
return $http.get("/users/" + user.id + "/score”);
}).then(function(result) {
$scope.score = result.data.total;
});
}
<div ng-controller="promiseCtrl">
<p>User: {{user.name}}</p>
<p>Score: {{score}}</p>
</div>
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
$timeout and $interval Jan, 2014 Copyright 2013, 2014, Uri Shaked
• $timeout(fn, delay) -> Promise
$timeout returns a promise that will be resolved once the
timeout is reached.
• $interval(fn, delay, count) -> Promise
$interval returns a promise which will be notified on each
iteration and will be resolved after count iterations if
count > 0.
Angular.JS, I Promise!
$timeout Example
function promiseCtrl($scope, $timeout, $http) {
$timeout(function(){
return $http.get("/status");
}, 1000).then(function(result) {
$scope.result = result.data
});
}
<div ng-controller="promiseCtrl">
{{result || "Preparing…"}
</div>
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Create Your Own
function requestFS(type, size) {
var deferred = $q.defer();
webkitRequestFileSystem(type, size,
function(fs) {
$rootScope.$apply(function() {
deferred.resolve(fs);
});
}, function (fsError) {
$rootScope.$apply(function() {
deferred.reject(fsError);
});
});
return deferred.promise;
}
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Wrapping as a Promise
You can wrap any value with a promise, using the utility
method $q.when().
$q.when(promise) → promise
$q.when(nonPromise) → a new promise, that will
asynchronously resolve to the given value nonPromise.
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Get & Return a Promise
function getFile(fs, name, options) {
var deferred = $q.defer();
$q.when(fs).then(function(resolveFS) {
resolveFS.getFile(name, options, function (file) {
$rootScope.$apply(function() {
deferred.resolve(file);
});
}, function (error) {
$rootScope.$apply(function() {
deferred.reject(error);
});
});
});
return deferred.promise;
}
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Usage Example
// Assume we also implemented readFileAsText() in a similar
// fashion.
function fileExampleCtrl($scope) {
var fs = requestFS(window.PERSISTENT, 1024*1024);
var file = getFile(fs, "log.txt");
readFileAsText(file).then(function(content) {
$scope.content = content;
});
}
<div ng-controller="fileExampleCtrl">
{{ content || 'Loading…' }}
</div>
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Promise Many
• Sometimes you want to wait for multiple promises
• Angular provide a utility method:
$q.all([promise, …]) → newPromise
• newPromise will resolve once all the given promises have
been resolved
• If any of the given promises is rejected, newPromise will
also be rejected
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Notify (Angular 1.2)
function downloadUserProfiles(users) {
var deferred = $q.defer(), promises = [];
angular.forEach(users, function(user) {
promises.push(
$http.get("/profile/" + user)
.then(function(result) {
deferred.notify(user);
return result.data;
}));
});
$q.all(promises).then(function(results) {
deferred.resolve(results);
});
return deferred.promise;
}
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
Gotcha’s
• You always want to call deferred.reject() and
deferred.resolve() within a scope context (e.g.
$rootScope.$apply()).
• Remember, you can resolve/reject a promise only once!
• Don’t forget to return deferred.promise
Jan, 2014 Copyright 2013, 2014, Uri Shaked
Angular.JS, I Promise!
About the Speaker
Uri Shaked, [email protected]
• Co-Organizer of Google Developer Group Tel-Aviv
• Salsa Instructor&Dancer, created the Salsa Beat Machine
• Musician, created the Zampoña mobile pan-flute app
• Loves to hack Electronics and Hardware
Jan, 2014 Copyright 2013, 2014, Uri Shaked