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

相關文章
相關標籤/搜索