Skip to content

Instantly share code, notes, and snippets.

@danthareja
Last active July 25, 2023 03:05
Show Gist options
  • Save danthareja/c9d7d4b598088ca1566c to your computer and use it in GitHub Desktop.
Save danthareja/c9d7d4b598088ca1566c to your computer and use it in GitHub Desktop.
A conjurer's guide to promises
/*
* A quick example of how to use Bluebird and Q to conjure your own promises
*
* Everything going on here is explained further in the following video:
* http://youtu.be/OU7WuVGSuZw?list=PLT-DLWOBKbB4dZ83I_7Ca-sUTvorckG-E
*
*/
// Import node modules
var Q = require('q');
var Promise = require('bluebird');
var GitHubApi = require('github');
// Instantiate GitHub API.
// NOTE: GitHub is just used for async demonstration purposes, you can imagine any other async functions throughout this example
var github = new GitHubApi({
version: '3.0.0'
});
/* * * * * A typical callback pattern * * * * * * */
var getUserAvatarWithCallback = function(user, callback) {
// Use our node-github library to search for a specific user. This is async so we have to pass it a callback.
github.search.users({ q: user }, function(error, response) {
// Couldn't find a user for some reason. Pass along the error with the regular node pattern (error, response)
if (error) { callback(error, null); }
else {
var avatarUrl = response.items[0].avatar_url; // We know the specific property of interest from the github API docs
callback(null, avatarUrl); // Pass along our avatarUrl with the regular node pattern (error, response)
}
});
};
// Invoke our function, passing it a callback that will accept the avatar url as it's only parameter
getUserAvatarWithCallback('danthareja', function(error, url) {
console.log('Got url with callback pattern', url);
});
/* * * * * Promises * * * * * * */
// ------- Bluebird -----------
var getUserAvatarWithBluebird = function(user) {
// Create and return a promise object using the 'new' keyword -> this is special to Bluebird's implementation
// Promises will be native in ES6 and will use the same syntax as Bluebird
return new Promise(function(resolve, reject) {
github.search.users({ q: user }, function(error, response) {
// Whatever is passed into reject gets can be accessed in the 'catch' block's callback function
if (error) { reject(error); }
else {
var avatarUrl = response.items[0].avatar_url;
// Pass arguments of interest into resolve
// Whatever is passed into resolve gets can be accessed in the 'then' block's callback function
resolve(avatarUrl);
}
});
});
};
// Invoke our 'promisified' function
getUserAvatarWithBluebird('danthareja')
.then(function(url) {
console.log('Got url with Bluebird promises', url);
})
.catch(function(error) {
console.log('Error getting avatar with q', error);
});
// ------------ Q -------------
var getUserAvatarWithQ = function(user) {
// Create a deferred object using Q's 'defer' constructor -> this is special to Q's implementation
var deferred = Q.defer();
github.search.users({ q: user }, function(error, response) {
if (error) { deferred.reject(error); } // deferred.reject syntax is special to Q
else {
var avatarUrl = response.items[0].avatar_url;
deferred.resolve(avatarUrl); // deferred.resolve syntax is special to Q
}
});
// The promise we want to return exists on Q's deferred object
return deferred.promise;
};
// Invoke our 'promisified' function
// Even though we use different syntax to create promises with bluebird and Q, the returned promise works exactly the same way
// i.e. they all have .then(), .catch(), .finally()
getUserAvatarWithQ('danthareja')
.then(function(url) {
console.log('Got url with q promises', url);
// Then's are chainable! Whatever we return in the previous 'then' gets passed into the next one
return url + ' SUPERAWESOMEAPPENDEDTEXT!!!!!! WOO!';
})
.then(function(appendedAvatarURL) {
console.log('Check out the new appended url', appendedAvatarURL);
// We can return promises in the chain too!
// Our next then function will only run after the promise is resolved.
// Herein lies the true power of promsies, play around with this to learn more!
var deferred = Q.defer();
// use setTimeout to simulate an aync process before the resolve. Anything asyncronous could happen here
setTimeout(function() {
deferred.resolve(appendedAvatarURL + ' and we did more async!');
}, 1000);
return deferred.promise;
})
.then(function(appendedAvatarURLAfterAnotherAsyncOperation) {
// This 'then' function will only execute after the promise returned from the previous function has been resolved
console.log('Now check out the even moar appended url! Woah!', appendedAvatarURLAfterAnotherAsyncOperation);
})
.catch(function(error) {
// Any error caught in a 'then' function above will break the chain and immediately run this function
console.log('Error somewhere in the getUserAvatarWithQ chain', error);
})
.finally(function() {
// the 'finally' function will always get run regardless if the promise was resolved (successful) or rejected (errored)
console.log('This will always get run');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment