-
Notifications
You must be signed in to change notification settings - Fork 3
PromiseJs
Once you work with Nodejs, you will write a lot of asynchronous code by callback functions. Using callback-passing for asynchronous actions does not compose very well and might create complex flows of passing callbacks around to handle return values. We are here making a simple example to see the reason why we use PromiseJs.
The task is to read content from a file, then encrypt it, then write it to the file.
Therefore, there are three functions to be applied:
- fs.readFile(filename, read_callback)
- encrypt(data, encrypt_callback)
- fs.writeFile(filename, content, write_callback)
We can define the simplest encrypt function as
var encrypt = function(data, callback) {
var encrypted_data = "";
len = data.length;
console.log(len);
for(var i=0; i<len; i++) {
encrypted_data += String.fromCharCode((data[i].charCodeAt()+1)%128);
}
return callback(null, encrypted_data);
};
Now we can apply these functions together to complete our task:
var fs = require('fs');
fs.readFile('./mocha.opts', 'utf8', function(read_err, read_data) {
if(read_err) return read_err;
console.log(read_data);
encrypt(read_data, function(encrypt_err, encrypted_data){
if(encrypt_err) return encrypt_err;
console.log(encrypted_data);
fs.writeFile('./encrypted_mocha.opts', encrypted_data, function(write_err) {
if(write_err) return write_err;
});
});
});
Since asynchronous coding in JS is implied by using callback function, which means we need to compose all the asyn functions by nesting them in callback function. The complexity of code will get higher and the readability will get worse.
If functions in the example return a value as synchronized code style. For example,
- read_value = fs.readFile(filename)
- encrypted_value = encrypt(data)
- write_value = fs.writeFile(filename)
Then we can get this tasks done by
fs.writeFile(encrypt(fs.readFile(filename)))
Even better, we can encapsulate these three functions into one object. Each of these functions return this object, then we can call these functions in a chain. Take synchronized functions for example,
var Oracle = function(initial_data) {
this.data = initial_data;
this.add = function(num) {
this.data += num;
return this;
}
this.square = function() {
this.data = this.data*this.data;
return this;
}
this.output = function() {
console.log(this.data);
return this;
}
};
o = new Oracle(3);
o.add(2).square().output().add(5).output();
In this case, we can write asynchronous code in a sequential way, which we used to. That is the idea of PromiseJs.
Simply speaking, what the PromiseJs is ? It is an object, most of time is a function, since javascript is an object-oriented function-based language. This object is call promise, which represents a value that may not be available yet. There are methods of this promise, and after those methods being called, they return promise object. The primary method for interacting with a promise is its then method.
There are more details in this document PromiseJs
Let's take the previous example, and rewrite into PromiseJs. Here, we use project q, which is an implementation of PromiseJs.
var encrypt = function (data) {
var encrypted_data = "";
len = data.length;
for(var i=0; i<len; i++) {
encrypted_data += String.fromCharCode((data[i].charCodeAt()+1)%128);
}
return encrypted_data;
}
var read_File = function(filename, encode) {
//return a promise object
return Q.nfcall(fs.readFile, filename, encode);
}
The task can be completed by
read_File('./mocha.opts', 'utf-8')
.then(encrypt)
.then(
function (data) {
return fs.writeFile('./encrypted_mocha.opts', data);
}
)
Or we can encapsulate the task into a promise object by
var task = function task () {
return read_File('./mocha.opts', 'utf-8')
.then(encrypt)
.then(
function (data) {
return fs.writeFile('./encrypted_mocha.opts', data);
}
)
}();
then if we want to read the content from './encrypted_mocha.opts', we can process
task
.then(function() {
return read_File('./encrypted_mocha.opts', 'utf-8');
})
.then(console.log);
Or without encapsulating the task, just add reading code by then method.
read_File('./mocha.opts', 'utf-8')
.then(encrypt)
.then(
function (data) {
return fs.writeFile('./encrypted_mocha.opts', data);
}
)
.then(function() {
return read_File('./encrypted_mocha.opts', 'utf-8');
})
.then(console.log);
Comparing with the nested code in callback function, PromiseJs provides better coding style. There are more in Q API and Q.