COMBO--组合拳打穿回调地狱~

我想应该会有不少像我同样的前端据说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

相关文章
相关标签/搜索