Loading
Richard Bateman
This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.
function getUserInfo(id, callback) {
$.ajax({
url: "/user/" + id,
type: 'GET',
dataType: 'html',
success: function(data, textStatus, xhr) {
callback(null, data);
},
error: function(xhr, textStatus, errorThrown) {
callback(new Error(textStatus), errorThrown);
}
});
}
function getUserComments(id, callback) {
$.ajax({
url: "/user/" + id + "/comments",
type: 'GET',
dataType: 'html',
success: function(data, textStatus, xhr) {
callback(null, data);
},
error: function(xhr, textStatus, errorThrown) {
callback(new Error(textStatus), errorThrown);
}
});
}
// Get the user info
getUserInfo(1, function(err, userDoc) {
if (!err) {
// get the user comments
getUserComments(1, function(err, commentArray) {
if (!err) {
DisplayUserInfoWithComments(userDoc, commentArray);
} else {
// Do something to handle errors
}
});
} else {
// Do something to handle errors
}
}
// Get the user info
var userDoc = null;
var commentArray = null;
var wasError = false
getUserInfo(1, function(err, inDoc) {
if (!err) {
userDoc = inDoc;
if (commentArray) {
DisplayUserInfoWithComments(userDoc, commentArray);
}
} else {
wasError = err;
wasError.errThrown = arguments[1];
}
});
getUserComments(1, function(err, inArray) {
if (!err) {
commentArray = inArray;
if (userDoc) {
DisplayUserInfoWithComments(userDoc, commentArray);
}
} else {
wasError = err;
wasError.errThrown = arguments[1];
}
});
// Deferred method
function getUserInfo(id) {
return $.ajax({
url: "/user/" + id,
type: 'GET',
dataType: 'html',
}).then(function(data, textStatus, xhr) { return data; });
}
function getUserComments(id, callback) {
$.ajax({
url: "/user/" + id + "/comments",
type: 'GET',
dataType: 'html'
}).then(function(data, textStatus, xhr) { return data; });
}
// Get the user info; deferred method
var userDfd = getUserInfo(1);
var commentsDfd = getUserComments(1);
DisplayUserInfoWithComments(userDfd, commentsDfd);
function DisplayUserInfoWithComments(userDfd, clistDfd) {
$.when(userDfd, clistDfd).then(function(user, clist){
// Previous contents of DisplayUserInfoWithComments in here
}, function(err) {
// Handle the error
});
}
Call fn when you are done.
Give me something I can use to get the result when I need it.
Aspects of code:
Express REST API made easy with promises
from bin/make_fake_data
function cleanDatabase() {
var waitFor = [];
console.log("Cleaning database");
waitFor.push(dropCollection('users'));
waitFor.push(dropCollection('comments'));
return Q.all(waitFor);
}
from bin/make_fake_data
var doneDfd = Q(null); // Initial resolved promise
doneDfd = doneDfd.then(cleanDatabase());
// create users
var userDfd, commentsDfd;
for (var i = 0; i < 20; ++i) {
// Note that since this is sequential, we wait on doneDfd
userDfd = doneDfd.then(createRandomUser);
commentsDfd = addCommentsForUser(userDfd);
// The result Promise for this iteration is then stored back in doneDfd
doneDfd = Q.all([userDfd, commentsDfd]);
}
doneDfd.then(function() {
console.log("All done!");
}, function(err) {
console.error("Everything has broken with the error: ", err);
});
This is certainly not as clean as a synchronous language -- but can you imagine doing all of this sequentially using callbacks?from bin/make_fake_data
function addCommentsForUser(userDfd) {
// Before we can add the comments, we need userDfd to resolve first.
// It is possible it may already be resolved, so we use Q.when which
// will make it a promise -- if it's a value, the promise will be immediately
// resolved.
return Q.when(userDfd).then(function(user) {
var savedObjects = [];
var comment;
for (var i = 0; i < 10; ++i) {
comment = new Comment();
comment.user = user;
comment.text = "I " + randgen(verbs) + " " + randgen(first_names);
savedObjects.push(Q.nbind(comment.save, comment)());
}
return Q.all(savedObjects);
});
}
Note that userDfd (dfd is a prefix I use, short for "Deferred", another name for a Promise) might or might not be a Promise. If it is a resolved User object this will work exactly the same.an npm module that understands promises
https://github.com/taxilian-promises/sendresponse
sendResponse was adapted from a library we use at GradeCam
This code comes from express-demo in routes/rest.jsrouter.get('/users', function(req, res) {
var userList = User.find().exec();
res.sendResponse(userList);
});
router.get('/users/:email', function(req, res) {
var user = getUserByEmail(req.params.email);
res.sendResponse(user);
});
router.get('/users/:email/comments', function(req, res) {
var userDfd = getUserByEmail(req.params.email);
var comments = getCommentsForUser(userDfd);
res.sendResponse(comments);
});
// Wrong way!
function getCoolStuff(someValue) {
var deferred = Q.defer();
setTimeout(function() {
if (Something_cool_happened) {
deferred.resolve(some_cool_value);
}
}, 500);
return deferred.promise;
}
// Right way!
function getCoolStuff(someValue) {
var deferred = Q.defer();
setTimeout(function() {
if (Something_cool_happened) {
deferred.resolve(some_cool_value);
} else {
deferred.reject(new Error("Nothing cool happened"));
}
}, 500);
return deferred.promise;
}
// Wrong way!
GetDBRecord().then(function(result) {
// Do something cool with the result
});
// Right way!
GetDBRecord().then(function(result) {
// Do something cool with the result
}, function(err) {
// Panic!
});
function displayLoadingIndicator(promise) {
showLoadingIndicator();
var hide = function() { hideLoadingIndicator(); };
Promise.when(promise).then(hide, hide);
}
function changeTheUser(user) {
return Promise.when(user).then(function(user) {
// The user is resolved!
});
}
function doSomethingAmazing(someValue) { return new Promise(function(resolve, reject) { CallAwesomeThing(someValue, function(err, result) {
if (err) { reject(err); } else { resolve(result); } }); }); }
The ECMAScript 6 is not Promises/A+ compliant, but is close.