Promises

As you can imagine, these weeks are pretty packed. Writing blog entries on a regular basis requires a certain amount of dedication, and it’s quite likely I won’t be posting as often a I once was. But every now and then I have a thought I’m mulling over, and I’ll try to stop in and share it.

At the moment, I’m really enjoying: JavaScript Promises!

We learned about them earlier this week, and for me, they just make everything better. I feel so much happier with the architecture of my code when I’m using them (I hated having to nest callbacks inside each other — it felt wrong somehow, ugly).

Just a quick example, here is how you might use a callback to open a file and then do something to it in Node.js:

// require the filesystem module
var fs = require(’fs’);
function doSomethingAwesome(fileData) {
// a bunch of operations to perform on data from a file;
}
// open the file
fs.readFile(’myFile.txt’, function(err, data) {
if (err) throw err;
// here's where things actually happen to it
doSomethingAwesome(data.toString())
});

This works — it takes advantage of JavaScript’s asynchronous behavior to continue the program while the file is loading, and then do some operation once the file is in working memory.

But what if you want to add a lot of logic? Well, ultimately, you can define the logic anywhere, but it must be called inside the callback. And worse, if your async function is going to rely on another async activity, then you have to nest async functions inside of each other. This can get kind of ugly, and to me, it just looks really disorganized. For example, look at this:

var http = require('http');
var fs = require('fs');
// open file
fs.readFile('myFile.txt', function(err, data) {
if (err) throw err;
  // then do something with the data
var newData = doSomething(data);
  // now write the file out
fs.writeFile('myFile.txt', newData, function(err, data) {
if (err) throw err;
    // now log something upon successful write
console.log('Write Successful');
    http.createServer(function(request, response) {
var file = './myFile.txt';
      // save the file and its info
var stat = fs.statSync(file);
      // set your HTTP headers
response.writeHead(200);
      // create a readstream 
var readStream = fs.createReadStream(file);
      // pipe that stream as the response 
readStream.pipe(response);
}).listen(3000);
});
});

Lets say you want to send the file using a server, as in the above example. The server has to go inside this writeFile callback, otherwise the file wouldn’t be finished writing to disk. You could define the code somewhere, and then call it inside the callback, but either way, you would have to nest the functionality inside these layers of callbacks. And if you forgot, something would go wrong, because data you’re waiting on will not have been received.

There is a better way. Promises.

Let’s say we had a function that returns a Promise for reading a file, and a function that returns a Promise for writing a file, and another function that runs our server, and they look like this:

var filePromise = promiseToReadFile('myFile.txt');
var writePromise = promiseToWriteFile('myFile.txt');
var serverPromise = promiseToDeliverFile('myFile.txt');

Each Promise will have a then method that takes a resolve and reject callback. If the process succeeds, the resolve callback is called. If it fails, the reject callback is called. And if we return promises, we can continue chaining these then statements. This may sound a bit abstract, so let’s see how that looks for the example we had above:

promiseToReadFile('myFile.txt')
.then(function(fileData){
var newData = doSomething(fileData);
  // return a promise to write the file
return promiseToWriteFile('myFile.txt', newData);
}, console.error)
.then(function() {
// and let's say the logic for starting the server is inside here
// we can rest assured it won't happen
// until the other 2 processes complete
return promiseToDeliverFile('myFile.txt');
}, console.error)
.then(function(server) {
// start listening on the server
server.listen(3000);
}, console.error)
.then(function(){
console.log('Successfully sent file!');
}, console.error));

You may have noticed that console.error is repeated a whole lot of times. In fact, the reject is optional — you can simply write one catch method at the end to catch the result of any reject event in the whole promise chain, which lets us to rewrite the above code like such:

promiseToReadFile('myFile.txt')
.then(function(fileData){
var newData = doSomething(fileData);
// return a promise to write the file
return promiseToWriteFile('myFile.txt', newData);
})
.then(function() {
// and let's say the logic for starting the server is inside here
// we can rest assured it won't happen
// until the other 2 processes complete
return promiseToDeliverFile('myFile.txt');
})
.then(function(server) {
// start listening on the server
server.listen(3000);
})
.then(function(){
console.log('Successfully sent file!');
})
.catch(console.error);

Tell me that’s not more readable than the nested callbacks.

In short, I ♥ Promises.

Any thoughts? Questions? Corrections? Feel free to comment below or send me an email: alexandra.polubiec@gmail.com


Originally published at paloobi.tumblr.com.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.