我想应该会有很多像我一样的前端听说js可以开发后台时,激动地踏上了node.js之路,这条路上第一个挑战,就是回调地狱。
app.get("/changePassword?**",function(req,res){
if(req.cookies.username){
pool.getConnection(function(err,connection){
if (err) {
console.log(err+"--from pool connection");
res.send("修改密码失败,数据库连接错误");
} else{
connection.query("USE userInfo",function(err,rows){
if (err) {
console.log(err+"--from using database");
res.send("修改密码失败,数据库使用错误");
} else{
var selectQuery = "SELECT * FROM users WHERE userName="+"'"+req.cookies.username+"'";
connection.query(selectQuery,function(err,rows){
if (err) {
console.log(err+"--from selectQuery");
res.send("修改密码失败,数据库查询错误");
} else{
if (req.query.password==rows[0].password) {
var updateQuery = "UPDATE users SET password="+"'"+req.query.newPassword+"' WHERE username="+"'"+req.cookies.username+"'";
connection.query(updateQuery,function(err,rows){
if (err) {
console.log(err+"--from updateQuery");
res.send("修改密码失败,数据库更新错误");
} else{
res.send("修改密码成功");
}
});/*connection.query update end*/
} else{
res.send("修改密码失败,原始密码错误");
}
}
});/*connection.query select end*/
}
});/*connection.query using database end*/
}
if(connection){connection.release()};
});/*pool.getConnection end*/
} else {
res.send("修改密码失败,登录失效");
}
});/*app.get end*/
这种造型的代码就是“邪恶金字塔”,或者说“回调地狱”,callback hell
我遇到的第一个障碍就是它,它让代码难以维护,难以修改,横向发展,非常不美观
于是我开始试图解决这个问题,为此,我求助了很多大神,看了很多帖子,被告知《ES6入门》这本书可以解决我的问题,于是从promise then到*yield到async/await
看到async/await我以为就皆大欢喜,问题解决了,然而nodejs目前需要babel转码才能使用async/await,很麻烦,而且对我这个新手很不友好。
在segmentfault上提问许久,发现有个asyncawait模块,可以模仿async/await模型来操作promise对象
npm install asyncawait
将如下代码添加到你的js文件中
var async = require("asyncawait/async");
var await = require("asyncawait/await");
var foo = async (function() {
var resultA = await (firstAsyncCall());
var resultB = await (secondAsyncCallUsing(resultA));
var resultC = await (thirdAsyncCallUsing(resultB));
return doSomethingWith(resultC);
});
await()里面可以放promise对象,也可以放异步回调函数,只要它有类似的返回机制,这样一来,就能提前使用async/await模式写代码了,一开始的回调地狱会变得如下代码一样,清晰易懂
//登录路由
app.get("/loginForm?**", async(function(req, res) {
try {
var connection = await(poolp.getConnection());
var selectQuery = "SELECT password FROM users WHERE username ='" + req.query.username + "'";
var rows = await(connection.query(selectQuery));
if (rows.length == 0) throw "登录失败,用户不存在";
if (rows[0].password != req.query.password) {
throw "登录失败,密码不正确";
} else {
res.send("登录成功");
}
} catch (err) {
res.send(err);
}
//记得释放connection,不然很快就会达到上限
if(connection) pool.releaseConnection(connection);
}));
不幸的是,await()里面放回调函数会使得代码很臃肿,如果放promise对象,就保持了与async/await模式的一致性。
nodejs的mysql模块,提供了pool,connection来操作数据库,可是它们都不是promise对象,我尝试自己封装成promise对象
var getConn = new Promise(function(resolve,reject){
pool.getConnection(function(err,connection){
if (err) {
reject(err);
} else {
resolve(connection);
}
});
});
var DBobj = function(connection){
this.connection = connection;
this.query = (queryString)=>{
var connPromise = new Promise(function(resolve, reject) {
this.connection.query(queryString, function(err, rows) {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
return connPromise;
};
return this;
};
很蛋疼,而且DBobj无法正确返回对象,不过国外有大神早就解决了这个问题
npm install promise-mysql
var mysqlp = require('promise-mysql');
poolp = mysqlp.createPool({
host: 'localhost',
user: 'root',
password: 'root',
database: 'userInfo',
connectionLimit: 10
});
就这么将mysql提供的对象转化为了promise对象,于是上面的登录路由就可以运行了,简洁明了,要加正则或者别的什么验证随时都能加,只需要在两行代码之间插入逻辑,再也不用框起一大片代码然后调缩进了!
相应的,fs模块,mail模块也应该有promise版本,大家可以去npm上面搜索
最后,我希望我的文章能帮助像我一样的小白打败回调地狱,一起踏上nodejs的探索之旅
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。