Skip to content

Instantly share code, notes, and snippets.

@kixxauth
Created March 31, 2012 17:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kixxauth/2267033 to your computer and use it in GitHub Desktop.
Save kixxauth/2267033 to your computer and use it in GitHub Desktop.
JavaScript Objects and Promises
//
// Typical Prototype composure in JS; This is the classical way of creating and
// using objects in JS.
//
// "Super" or "Parent" object prototype
function Cat(color) {
this.color = color;
this.saying = 'meowwww';
}
Cat.prototype = {
speak: function () {
return this.saying;
},
hasTail: function () {
return true;
}
};
// "Sub" or "Child" object prototype
function Kitten() {
this.color = color;
}
Kitten.prototype = new Cat();
Kitten.prototype.speak = function () {
return 'mewww';
};
var fifi = new Kitten('grey');
fifi.speak();
//
// A "somewhat new" and "maybe better" way of Object construction in JS:
//
// We need a special function to extend super types.
function extend(m) {
var m;
for (m in parent) {
if (!this.hasOwnProperty(m)) {
this[m] = parent[m];
}
}
}
// The "trick" is the new `Object.create()` method found in ECMAScript 5
// comlient JS environments.
var Cat = {
speak: function () {
return this.saying;
},
hasTail: function () {
return this.tail;
},
create: function (color) {
var self = Object.create(Cat);
self.color = color;
self.saying = 'meowwww';
self.tail = true;
return self;
}
};
var Kitten = {
speak: function () {
return 'mewww';
},
create: function (color) {
var self = Object.create(Kitten);
self.color = color;
self.tail = true;
return self;
},
extend: extend
};
Kitten.extend(Cat);
var fifi = Kitten.create('grey');
fifi.speak();
//
// Taking better advantage of Prototypal Composure and adding private members
// using "create" or "maker" functions.
// AKA: The Module Pattern
//
function speak() {
return this.saying;
}
function hasTail() {
return this.tail;
}
function createCat(color) {
var self = {color: color, tail: true, saying: 'meowww'};
self.hasTail = hasTail;
self.speak = speak;
return self;
}
function createKitten(color, secret) {
var self = {color: color, tail: true};
self.hasTail = hasTail;
self.speak = function () {
return 'mewww';
};
self.revealSecret = function () {
return secret;
};
return self;
}
var fifi = createKitten('grey', "I'm really a baby tiger.");
fifi.speak();
fifi.revealSecret();
// Problem? The user can change the object on the fly, and we might not want
// then to do this.
fifi.tail = false;
fifi.hasTail();
// However, when we want to have a secret, we can expose it privately with
// the "maker" function.
fifi.secret = "An evil secret"; // User tries to change the secret.
fifi.revealSecret() // Will return "I'm really a baby tiger."
// Takeaway:
// The user cannot change the secret, in any way.
// Problem? What happens when we call these methods? What will `this` be?
speak();
hasTail();
// Takeaway:
// You should never call these globally defined methods unless they have been
// attached to an object, otherwise `this` will be set as the global `window`
// object.
//
// The Callback Spaghetti or "Pyramid of Doom" problem.
//
$('button').click(function () {
getTwitterUsers(function (err, users) {
if (err) {
return alert(err);
}
var twitterUsers = '';
function getMore() {
if (!users.length) {
$('ul').html(twitterUsers);
return;
}
var id = users.shift();
getTwitterUser(id, function (err, userData) {
if (err) {
return alert(err);
}
twitterUsers += ('<li>'+ userData +'</li>');
getMore();
});
};
getMore();
});
});
//
// Refactor the feature and compose it with reusable functions.
//
function composeUserHTML(users, callback) {
var twitterUsersHTML = '';
function getMore() {
if (!users.length) {
return callback(twitterUsersHTML);
}
var id = users.shift();
getTwitterUser(id, function (err, userData) {
if (err) {
return alert(err);
}
twitterUsersHTML += ('<li>'+ userData +'</li>');
getMore();
});
}
getMore();
}
function updateUsersDOM(html) {
$('ul').html(twitterUsers);
}
function updateTwitterUsers() {
getTwitterUsers(function (err, users) {
if (err) {
return alert(err);
}
composeUserHTML(users, updateUsersDOM);
});
}
$('button').click(updateTwitterUsers);
//
// Or use Promises to flatten the pyramid of doom
//
// Using the "Q" API (https://github.com/kriskowal/q)
// OR the jQuery Deferred API (http://api.jquery.com/category/deferred-object/)
//
function updateTwitterUsers() {
getTwitterUsers()
.then(composeUserHTML)
.fail(handleFailure);
}
$('button').click(updateTwitterUsers);
function composeUserHTML(users) {
var deferred = Q.defer(),
twitterUsersHTML = '';
function getMore() {
if (!users.length) {
return deferred.resolve(twitterUsersHTML);
}
var id = users.shift();
getTwitterUser(id, function (err, userData) {
if (err) {
return deferred.reject(err);
}
twitterUsersHTML += ('<li>'+ userData +'</li>');
getMore();
});
}
getMore();
return deferred.promise
}
function handleFailure(err) {
alert(err.toString());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment