Bluebird Promises

Soni Pandey
4 min readSep 30, 2016

--

Everyone, who is in the era of JavaScript |Node and other JavaScript based technology, they are very much familiar with callbacks and they know how to implement it. Just have a look on Working with callback APIs using the Node convention: Returns a function that will wrap the given nodeFunction. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. In Node/io.js most APIs follow a convention of ‘error-first, single-parameter’ as such:

nodeFunction(function(error, result){
if(error){
return error;
}
return result;
})
nodeFunction(callback){
/**Please don't concentrate on calling function */
SOMEAPICALL(function(err, res){
if(err) callback("error", null);
callback(null, "success");
})
}

The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. If the nodeFunction calls its callback with multiple success values, the fulfillment value will be the first fulfillment item.Why should we use promises in node?

Here’s how you would tackle the problem with promises:

fs.readFileAsync('directory/file-to-read')
.then(function(fileData){
return fs.mkdirAsync('directory/new-directory');
})
.then(function(){
return fs.writeFileAsync('directory/new-directory/message.txt');
})

Promises provide us with a cleaner and more robust way of handling async code. Instead of using a callback, the initial function returns a promise which is ‘thenable’. ‘.then’ can be chained as many times as you like and each ‘.then’ provides you with the information returned from the previous ‘.then’. Anything returned from a ‘.then’ will itself be ‘thenable’. This will usually be another async call, like in my example.

Promises also easily enable you to separate out your code (you will want to do this a lot with your node application) into different files. For example:

function readFileandMakeDirectory(){
return fs.readFileAsync('directory/file-to-read')
.then(function(fileData){
return fs.mkdirAsync('directory/new-directory');
});
}
//The following will execute once the file has been read and a new directory has been made
readFileandMakeDirectory()
.then(function(){
return fs.writeFileAsync('directory/new-directory/message.txt');
})

You see that it’s easy to create functions that return a promise. This is exceptionally useful when you’re splitting your code into different files with module.exports.
But the module I want to use does not return a promise: You will have noticed that in my examples above I used such methods as ‘fs.writeFileAsync’ and ‘fs.mkdirAsync’. If you check the node documentation you will see that these methods don’t actually exist. FS does not return promises.

Despite this, bluebird has the incredibly useful functionality of enabling you to ‘promisfy’ modules which do not return promises. For example, to promisfy the fs, mongoose, mongodb, etc. module, simply require bluebird and a promisified version of fs.

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var mongoose = Promise.promisifyAll(require('mongoose'));

Here, we are talking about bluebird:

bluebird comes with a fast and efficient way to convert them to promise based APIs through the Promise.promisify and Promise.promisifyAll function calls.

Promise.promisify — converts a single callback taking function into a promise returning function. It does not alter the original function and returns the modified version.

Promise.promisifyAll — takes an object full of functions and converts each function into the new one with the Async suffix (by default). It does not change the original functions but instead adds new ones.

Here is an example with an API that lets us know when when a connection is ready. The attempt here is imperfect and we’ll describe why soon:

function connect() {
var connection = myConnector.getConnection(); // Synchronous.
return new Promise(function(resolve, reject) {
connection.on("ready", function() {
// When a connection has been established
// mark the promise as fulfilled.
resolve(connection);
});
connection.on("error", function(e) {
// If it failed connecting, mark it
// as rejected.
reject(e); // e is preferably an `Error`.
});
});
}

The problem with the above is that getConnection itself might throw for some reason and if it does we’ll get a synchronous rejection. An asynchronous operation should always be asynchronous to prevent double guarding and race conditions so it’s best to always put the sync parts inside the promise constructor as such:

function connect() {
return new Promise(function(resolve, reject) {
// If getConnection throws here instead of getting
// an exception we're getting a rejection thus
// producing a much more consistent API.
var connection = myConnector.getConnection();
connection.on("ready", function() {
// When a connection has been established
// mark the promise as fulfilled.
resolve(connection);
});
connection.on("error", function(e) {
// If it failed connecting, mark it
// as rejected.
reject(e); // e is preferably an `Error`
});
});
}

Working with delays/setTimeout

There is no need to convert timeouts/delays to a bluebird API, Returns a promise that will be resolved with value (or undefined) after given ms milliseconds. If value is a promise, the delay will start counting down when it is fulfilled and the returned promise will be fulfilled with the fulfillment value of the value promise. If value is a rejected promise, the resulting promise will be rejected immediately.

Promise.delay(1000).then(function() {
console.log("1000 ms passed");
return "Hello world";
}).delay(1000).then(function(helloWorldString) {
console.log(helloWorldString);
console.log("another 500 ms passed") ;
});

I am also new for this, I just tried in one project and saw its response is very fast and efficient. Thanks to Bluebird Team for such a great way to provide, handling callback, Runs everywhere and Easy to debug. You can have a look to installation steps for bluebird:

You can reach out to me for any doubt and suggestions. Please share this with others as well.

Oh, and if you like this, click the 💚 below. Keep watching this space and follow me for more articles on technology.

Thanks!
Happy Coding!!

--

--

Soni Pandey
Soni Pandey

Written by Soni Pandey

I am a Node.js Developer and eager to learn new technology. I blog, tweet & read whenever I can.

Responses (1)