Preface
The biggest highlight of Nodejs is the event-driven, non-blocking I/O model, which makes Nodejs have strong concurrency processing capabilities and is very suitable for writing network applications. Most I/O operations in Nodejs are almost asynchronous, that is, the results of our I/O operations basically need to be processed in the callback function, such as the following function that reads the content of the file:
The code copy is as follows:
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
So, what should we do if we read two files and merge the contents of these two files together? Most people who are not in contact with JS may do this:
The code copy is as follows:
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
fs.readFile('/etc/passwd2', function (err, data2) {
if (err) throw err;
// Process data of data and data2 here
});
});
If you deal with multiple similar scenarios, wouldn’t it be that the callback functions are nested layer by layer? This is what people often call back pyramid or callback hell (http://callbackhell.com/), and it is also the most troublesome problem for JS novice.
This kind of nested code has brought many problems to development, mainly reflected in:
1. The possibility of code becomes worse
2. Debugging difficulties
3. It is difficult to check after an exception occurs
This article mainly introduces how to handle the above asynchronous callback issues elegantly.
Primary solution: Recursively process asynchronous callbacks
We can use recursion as the execution control tool for the code. Encapsulate the operations that need to be executed into a function, and control the execution process of the code by recursively calling in the callback function. Without further ado, let’s talk about nonsense, let’s take a look at the previous code:
The code copy is as follows:
var fs = require('fs');
// List of files to be processed
var files = ['file1', 'file2', 'file3'];
function parseFile () {
if (files.length == 0) {
return;
}
var file = files.shift();
fs.readFile(file, function (err, data) {
// Process file data here
parseFile(); // After processing, process the next file through recursive call
});
}
// Start processing
parseFile();
The above code has processed the files in the array in turn as an example, introducing the execution process of controlling the code through recursive means.
It is good to apply it to some simple scenarios, such as: we can use this method by saving the data in an array into the database in turn.
Recursively, some simple asynchronous callback problems can be solved. However, it still seems powerless to deal with complex asynchronous callbacks (such as synchronizing the results of multiple asynchronous operations).
Gorgeous point: Use third-party libraries such as Async, Q, Promise to handle asynchronous callbacks
In order to better handle nested callbacks, you can consider using some third-party libraries that specifically deal with asynchronous. Of course, if you have the ability, you can write an auxiliary tool for asynchronous processing by yourself.
The most commonly used libraries for handling asynchronous processing are: async, q and promise. Judging from the npmjs.org website, async is the most popular. I have used async before, and it is indeed quite convenient, and various asynchronous processing control flows are implemented well.
We will use async to process the code that initially reads two files at the same time, as shown below:
The code copy is as follows:
var async = require('async')
, fs = require('fs');
async.parallel([
function(callback){
fs.readFile('/etc/passwd', function (err, data) {
if (err) callback(err);
callback(null, data);
});
},
function(callback){
fs.readFile('/etc/passwd2', function (err, data2) {
if (err) callback(err);
callback(null, data2);
});
}
],
function(err, results){
// Process data of data and data2 here, and the content of each file is obtained from results
});
Through the async module, the asynchronous execution process can be well controlled, which can also solve the problem of layered callbacks. The code is clearer than before, but it still cannot be separated from the callback function.
Think about it, it would be great if you can handle asynchronous without using callback functions. Next, let’s talk about using the new features of ES6 to achieve this goal.
Elegant point: embrace ES6, replace callback functions, and solve callback hell problem
By the way, EcmaScript Harmony (ES6) has introduced a lot of new features to js. Students who don’t know much about ES6 can take a Baidu look on their own.
To use the new features of ES6 in nodejs, you need to use v0.11.x or above.
This article introduces the use of Generator feature instead of callback functions. Don’t know about Generator? You can check it out here.
Here we use two modules co and thunkify, and we use the npm install command to install it.
Take the problem mentioned at the beginning of this article as an example. The example code using the generator feature is as follows:
The code copy is as follows:
var fs = require('fs')
, co = require('co')
, thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
co(function *() {
var test1 = yield readFile('test1.txt');
var test2 = yield readFile('test2.txt');
var test = test1.toString() + test2.toString();
console.log(test);
})();
It is also very simple to handle exceptions in the code, just do it this way:
The code copy is as follows:
try {
var test1 = yield readFile('test1.txt');
} catch (e) {
// Handle exceptions here
}
Is this kind of code much more elegant? Isn't it great to handle asynchronously like writing synchronous code?
The most popular framework for web development in the nodejs field is Express. It is worth mentioning that the core member of Express TJ, the great master of Express, has led a new web framework - koa, which claims to be the next generation of web development framework. Koa really uses the feature of ES6 generator to help us avoid falling into layers of callbacks when developing the web system.
Summarize
Quote a sentence from the fibjs project promotion: Less Callback, More Girls - Less Callbacks, More Girls