Adam Terlson
Software Engineer
var result = null;
$.get('/echo/json', function (resp) {
result = resp;
});
console.log('result: ', result);
// result: null
$.get('...', function (resp) {
// Use the single response
});
function getPart1(callback) {
$.get('...', callback);
}
function getPart2(callback) {
$.get('...', callback);
}
function getPart3(callback) {
$.get('...', callback);
}
getPart1(function () {
getPart2(function () {
getPart3(function () {
// Add value
});
});
});
var responses = [];
function somethingResponded(resp) {
responses.push(resp);
if (responses.length === 3) {
someOtherFunction(responses);
}
}
function getPart1(callback) {
$.get('firstURL', callback);
}
function getPart2(callback) {
$.get('secondURL', callback);
}
function getPart3(callback) {
$.get('thirdURL', callback);
}
getPart1(somethingResponded);
getPart2(somethingResponded);
getPart3(somethingResponded);
app.get('/', function(req, res) {
// Open the connection.
db.open(function(err, db) {
// Get one collection.
db.collection('users', function(err, usersColl) {
// Search the first collection.
usersColl.find({}, {'limit': 3}, function(err, usersCursor) {
// Convert the result into an array.
usersCursor.toArray(function(err, users) {
// Get the second collection.
db.collection('articles', function(err, artColl) {
// Search the second collection.
artColl.find({}, {'limit': 3}, function(err, artCursor) {
// Convert the result into an array.
artCursor.toArray(function(err, articles) {
// Now we have two arrays, users and articles, in scope.
// Render the home page.
res.render('home.ejs', {'users': users, 'articles': articles});
function populateModel(model, callback) { // Make sure callback is defined if (!callback) callback = function () { }; $.get('/api/model', { success: function (data) { model.data = data; callback(null, data); }, error: function (err) { // Have to always call callback, maybe it needs to happen // but maybe it'll break if we do, so we need to go to // node-style callbacks everywhere callback(err); } }); }
populateModel(modelA, function () { console.log(modelA.data); }); populateModel(modelB);
fs.readdir('/some/path', function (err, fileNames) {
// Often forgotten or ignored: handling err
if (err) throw "We're screwed";
fileNames.forEach(function (name) {
fs.readFile(name, function (fileErr, file) {
// Especially when doing nested or more complex flows
if (fileErr) throw "This is getting seriously annoying";
// Add real value here
});
});
});
then
) to transform the state of the promise
then
returns a new promise.then(onDone, onFail)
$.get('...').then(function () {
// Success Method
});
function getPart1() {
return $.get('...');
}
function getPart2() {
return $.get('...');
}
function getPart3() {
return $.get('...');
}
getPart1()
.then(getPart2)
.then(getPart3);
$.when()
Q.all()
in jQuery
$.when($.get(...), 2, $.get(...), 4)
.then(function (one, two, three, four) { })
Q
Q.all([promise1, 2, promise3, 4]).then(function (arrResults) { } )
Non promises are treated as resolved promises of that value.
function getPart1() {
return $.get('...');
}
function getPart2() {
return $.get('...');
}
function getPart3() {
return $.get('...');
}
$.when(getPart1(), getPart2(), getPart3())
.then(function () {
console.log('All done!');
});
in QQ.all([getPart1(), getPart2(), getPart3()])
.then(function () {
console.log('All done!');
});
.resolve(value) // Means to resolve
.reject(reason) // Means to reject
.promise() // Means to construct promise
function resolveAfterOneSecond(message) {
var dfd = $.Deferred();
setTimeout(function () {
dfd.resolve(message);
}, 1000);
return dfd.promise();
}
in Qfunction resolveAfterOneSecond(message) {
var dfd = Q.defer();
setTimeout(function () {
dfd.resolve(message);
}, 1000);
return dfd.promise;
}
var dfd = Q.defer(),
promise = dfd.promise;
dfd.resolve('Yay Happy!');
promise.then(function (result) {
console.log(result); // Yay Happy!
});
var promise = Q(10)
.then(function (num) {
return num+1;
}).then(function (num) {
console.log(num); // 11
});
promise.then(function (num) {
console.log(num); // undefined!
});
var root = Q(10);
var parent = root.then(function () {
var child = Q(20);
return child;
});
parent.then(function (num) {
console.log(num); // 20
});
var dfd = Q.defer();
dfd.reject(new Error('Thank you Todd')); // Can reject with any value
var promise = Q()
.then(someFunction)
.then(function bomber() {
throw new Error('Unexpected!');
})
.then(notcalled)
.then(stillnotcalled);
console.log(promise.isRejected()); // True
promise
.then(doSomething)
.then(doSomethingElse, function errorHandler(err) {
console.log('You experienced an error!');
});
});
// In Q only:
promise
.then(doSomething)
.then(doSomethingElse)
.catch(function errorHandler(ex) {
console.log('You experienced an error!');
});
});
var dfd = $.Deferred();
dfd
.then(function () {
throw new Error('Unexpected');
}).then(
function done() {
// ...
},
function fail() {
console.log('sad, but happy'); // <---- Not this one.
}
);
try {
dfd.resolve();
} catch (ex) {
console.log('THE WORST'); // <---- This one.
}
But if you already have it at a dependency, it's good.done()
app.get('/user/:id', function (req, res) {
getUser(req.params.id)
.then(populateUserDocuments)
.then(res.json) // json response helper
.done(); // Will cause any unhandled exceptions to throw
});
"This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. "
var promise1 = resolveAfterOneSecond('Hello'); // Makes a promise resolved with 'Hello'
var promise2 = promise1.then(function (resp) {
console.log('A: ' + resp);
});
// A: Hello
var promise1 = resolveAfterOneSecond('Hello');
var promise2 = promise1.then(function (resp) {
console.log('A: ', resp);
});
promise2.then(function (resp) {
console.log('B: ', resp);
});
// A: Hello
// B: Undefined
var promise1 = resolveAfterOneSecond('Hello');
var promise2 = promise1.then(function (resp) {
console.log('A: ' + resp);
return "Goodbye";
});
promise2.then(function (resp) {
console.log('B: ' + resp);
});
// A: Hello
// B: Goodbye
var promise1 = resolveAfterOneSecond('Hello');
var promise2 = promise1.then(function (resp) {
console.log('A: ', resp);
return resolveAfterOneSecond('Goodbye');
})
promise2.then(function (resp) {
console.log('B: ', resp);
});
// A: Hello
... One second later
// B: Goodbye
var promise1 = resolveAfterOneSecond('Hello'); // Makes a promise resolved with 'Hello' var promise2 = promise1.then(function (firstResp) { return resolveAfterOneSecond('Goodbye') .then(function (secondResp) { return firstResp + ' ' + secondResp;
}); }); promise2.then(function (resp) { console.log('A: ', resp); });
// A: Hello Goodbye
var promise1 = resolveAfterOneSecond('Hello');
var promise2 = promise1
.then(function (resp) {
throw 'Whoopsies!'
});
promise2.then(function (resp) {
console.log('A: ' + resp);
});
// Nothing!
function entireWorkflow(callback) {
fs.readdir('/some/path', function (err, fileNames) {
// Often forgotten or ignored is handling err
if (err) throw "We're screwed";
fileNames.forEach(function (name) {
fs.readFile(name, function (fileErr, file) {
// Especially when doing nested or more complex flows
if (fileErr) throw "This is getting seriously annoying";
// Add real value here
});
});
// Uhh... when do I execute callback?
});
}
var readdir = Q.denodeify(fs.readdir),
readfile = Q.denodeify(fs.readFile);
function entireWorkflow() {
return readdir('/some/path')
.then(function (fileNames) {
// Convert array of file names into array of promises
var arrPromises = fileNames.map(function (name) {
// returned objects to push into the new array
return readfile(name).then(function (file) {
// ...
});
});
return Q.all(arrPromises);
}).catch(function (ex) {
// Exception handling for entire process
});
}
// Entirely async
getDocument()
.then(function (document) {
var status = determineStatus(document);
return validatePublishReady(status);
})
.then(publishDocument)
.then(function (publishResponse) {
console.log("Response: " + publishResponse);
})
.catch(function (err) {
console.log("Publishing error: " + err);
});
// Entirely blocking
try {
var document = getDocument();
var status = determineStatus(document);
var publishResponse = publishDocument(validatePublishReady(status));
console.log("Response: " + publishResponse);
}
catch(err) {
console.log("Publishing error: " + err);
}
function populateModel(model, callback) {
// Make sure callback is defined
if (!callback) callback = function () { };
$.get('/api/model', {
success: function (data) {
model.data = data;
callback(null, data);
},
error: function (err) {
// Have to always call callback, maybe it needs to happen
// but maybe it'll break if we do, so we need to go to
// node-style callbacks everywhere
callback(err);
}
});
}
populateModel(modelA, function () { console.log(modelA.data); });
populateModel(modelB);
function populateModel(model) {
// What happens next is out of scope
// Clean unit for testing!
return $.get('/api/model')
.then(function (data) {
model.data = data;
return model;
});
}
populateModel(modelA)
.then(function (model) { console.log(model.data); });
populateModel(modelB); // Does nothing with the promise and that's okay!
Q().nodeify()
to support both easily!
$http().then()
and when to use $http.success()
// Set the default implementation of `Backbone.ajax` to proxy through to `$`.
// Override this if you'd like to use a different library.
Backbone.ajax = function() {
return Backbone.$.ajax.apply(Backbone.$, arguments);
};
Backbone.sync = function(method, model, options) {
...
// Make the request, allowing the user to override any Ajax options.
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
model.trigger('request', model, xhr, options);
return xhr;
};
Q.nfcall(FS.readFile, "foo.txt", "utf-8").then(function (text) {
});
module.exports = exports = function MyModule(someArg, callback) {
return doSomethingAsync(someArg)
.then(doSomethingElseAsync)
.then(doAnotherThingAsync)
.nodeify(callback); // Will execute callback in [err, result] fashion if present
};
"Promises are so fundamentally useful that somehow Mozilla, Google, Apple, Microsoft, Adobe, Facebook, and others all agreed it was important enough to build into the language."
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
promise.then(function () {
// success
}, function () {
// failure
});
$.when and Q.all equivalentPromise.all([somevalue, apromise]).then(function(values) {
// values == [ true, 3 ]
});
describe('User', function () {
describe('#save()', function () {
it('should save without error', function (done){
var user = new User('Luna');
user.save().then(function (res) {
// Assertions here
done();
}, function (err) {
done(err);
});
})
})
})
it("should be fulfilled with 5", function () {
// Return your promise, rather than executing a done callback
return promise.then(function (result) {
return result.should.equal(5);
});
});
it("should be fulfilled with 5", function () {
return promise.should.become(5);
});
By Adam Terlson
An overview of JavaScript promises (specifically in jQuery and Q) packed full of code examples. Will make you never use callbacks again.