我想应该会有不少像我同样的前端据说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对象node
npm install asyncawait
将以下代码添加到你的js文件中mysql
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模式写代码了,一开始的回调地狱会变得以下代码同样,清晰易懂sql
//登陆路由 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
npm install promise-mysql
var mysqlp = require('promise-mysql'); poolp = mysqlp.createPool({ host: 'localhost', user: 'root', password: 'root', database: 'userInfo', connectionLimit: 10 });
就这么将mysql提供的对象转化为了promise对象,因而上面的登陆路由就能够运行了,简洁明了,要加正则或者别的什么验证随时都能加,只须要在两行代码之间插入逻辑,不再用框起一大片代码而后调缩进了!segmentfault
相应的,fs模块,mail模块也应该有promise版本,你们能够去npm上面搜索promise
最后,我但愿个人文章能帮助像我同样的小白战胜回调地狱,一块儿踏上nodejs的探索之旅babel