Suppose a business scenario:
Through the rss address, get the rss and save it in the file, and the rss address is saved in the file.
To complete the business in this scenario, three tasks are required:
1. Read the rss address from the file.
2. Get rss.
3. Save in file.
Finally, these three tasks are integrated.
Prepare:
Files that store rss address, address.txt.
http://programmer.csdn.net/rss_programmer.html
Task 1:
Read the contents of the rss address file and return via callback.
The code copy is as follows:
var getRssAddress = function(path, callback) {
fs.readFile(path, {encoding: 'utf8'}, function (err, data) {
callback(err, data);
});
}
Task 2:
Get to rss through the rss address and return error or data via callback.
The code copy is as follows:
var getRss = function(url, callback) {
var data = '';
http.get(url, function(res) {
res.on('data', function(chrunk) {
data += chrunk;
});
res.on('end', function() {
callback(null, data);
});
}).on('error', function(err) {
callback(err, null);
});
}
Task 3:
Save rss in a file and return an error via callback.
The code copy is as follows:
var saveRss = function(data, callback) {
fs.writeFile('rss.txt', data, 'utf8', function(err) {
callback(err);
});
}
Integration:
The code copy is as follows:
getRssAddress('address.txt', function(err, data) {
if(err) {
console.log(err);
return;
}
getRss(data, function(err, data) {
if(err) {
console.log(err);
return;
}
saveRss(data, function(err) {
if(err) console.log(err);
});
});
});
The above code is fully asynchronous processing. The most common callback is used to handle the return of asynchronous logic. The advantage is that the standard writing method is easy for everyone to accept; the disadvantage is that the coupling is too strong, the handling of exceptions is that the code is not intuitive, especially when dealing with complex business logic and many tasks, the layered callbacks will make people look at them and the code is difficult to maintain.
One of the implementations of the Promise/A specification is when.js, which is aimed at such a problem domain.
Let's take a look at the modified code.
Task 1:
The code copy is as follows:
var getRssAddress = function(path) {
var deferred = when.defer();
fs.readFile(path, {encoding: 'utf8'}, function (err, data) {
if (err) deferred.reject(err);
deferred.resolve(data);
});
return deferred.promise;
}
Task 2:
The code copy is as follows:
var getRss = function(url) {
var deferred = when.defer();
var data = '';
http.get(url, function(res) {
res.on('data', function(chrunk) {
data += chrunk;
});
res.on('end', function() {
deferred.resolve(data);
});
}).on('error', function(err) {
deferred.reject(err);
});
return deferred.promise;
}
Task 3:
The code copy is as follows:
var saveRss = function(data) {
var deferred = when.defer();
fs.writeFile('rss.txt', data, 'utf8', function(err) {
if(err) deferred.reject(err);
deferred.resolve();
});
return deferred.promise;
}
Integration:
The code copy is as follows:
getRssAddress('address.txt')
.then(getRss)
.then(saveRss)
.catch(function(err) {
console.log(err);
});
explain:
The "Deferred/Promise" model defined by the promise/A specification is the "Publish/Subscriber" model. Publishing events through the Deferred object can be a completion resolve event or a failed reject event; corresponding completed or failed subscriptions are made through the Promise object.
In the Promises/A specification, each task has three states: default (pending), fulfilled (fulfilled), and failed (rejected).
1. The default state can be transferred to the completion state in one direction. This process is called resolve, and the corresponding method is deferred.resolve(promiseOrValue);
2. The default state can also be transferred to the failed state in one direction. This process is called reject, and the corresponding method is deferred.reject(reason);
3. In the default state, you can also declare task execution information through deferred.notify(update), such as execution progress;
4. The state transfer is one-time. Once the task changes from the initial pending to other states, it will enter the execution process of the next task.
Follow the above code.
Define a deferred object through when.defer.
var deferred = when.defer();
After the asynchronous data is successfully obtained, a completion event is published.
deferred.resolve(data);
After the asynchronous data acquisition fails, a failed event is published.
deferred.reject(err);
And return the Promise object as a subscription.
return deferred.promise;
Subscription is a subscription that is completed/failed/notified through the then method of the Promise object.
getRssAddress('address.txt')
.then(getRss)
Then there are three parameters, namely onFulfilled, onRejected, and onProgress
promise.then(onFulfilled, onRejected, onProgress)
The previous task is resolve(data), and the onFulfilled function will be triggered, and data will be used as its parameter.
If the previous task is rejected (reason), then onRejected will be triggered and a reason will be received.
At any time, only one of onFulfilled and onRejected can be triggered and only once.
For handling exceptions, when.js also provides an extremely convenient method. Then can pass errors. When multiple tasks are executed serially, we can only define onRejected in the last then. You can also call the catch function after the last then to catch any task exception.
This is a simple and clear way to write it.
The code copy is as follows:
getRssAddress('address.txt')
.then(getRss)
.then(saveRss)
.catch(function(err) {
console.log(err);
});
Promise brings great convenience to asynchronous programming, allowing us to focus on the implementation of a single task without falling into the pyramid misfortune. The above code is only basic use. When.js provides much more than the functions mentioned in this article, please refer to the official API for details.