我的NodeJS學習之路9(改善代碼)

請關(guān)注專題:我的NodeJS學習之路(實踐之路)

小弟初涉node領(lǐng)域,不足之處,還請多多指教!
歡迎Star、Fork:https://github.com/gefangshuai/ANodeBlog

今天是不幸的一天,為什么說呢,因為Github掛了!全球最大的同性交友網(wǎng)站掛了,讓我等技術(shù)宅還怎么好好的擼代碼呢?

好了,閑篇少扯,說點正事吧。今天我們來介紹程序中用到的幾個強大的中間件。

async - 強大的異步功能支持

之前已經(jīng)簡單介紹過,請移步NodeJS異步流程控制簡單介紹。為什么要將這個中間件呢,因為當你接觸nodejs代碼多了之后,難免會受到“回調(diào)之痛”。各種的回調(diào)嵌套真的把你給玩壞了。代碼看起來就好像多層的if-else嵌套一樣。

比如我們做用戶注冊功能,保存用戶之前,要先判斷一下用戶名是否已經(jīng)存在,大致代碼如下:

var user = req.body;
var User = dbHelper.User;   
User.findOne({username: user.username}, function (err, doc) {
    if(err){
        next(err);
    }else{
        if(doc){    // 用戶名已被占用
            req.flash('error', '用戶名已被占用');
            res.redirect('/reg');
        }else{
            User.create(user, function (err, doc) {
                if(err){
                    next(err);
                }else{
                    req.flash('success', '注冊成功,請登錄!');
                    res.redirect('/login');
                }
            });
        }
    }
});

對于數(shù)據(jù)庫的操作我們嵌套了兩層。再進一步,加入保存成功后,自動為注冊用戶綁定一些數(shù)據(jù)并存到數(shù)據(jù)庫,同時在跳轉(zhuǎn)成功的頁面進行展示呢?是不是又要多嵌套兩層?這時候我們的代碼已經(jīng)面目全非了!

這時候改async出場了。
async將各種嵌套的異步進行有效組織,增加了代碼的可維護性(雖然是為 Node.js 設(shè)計的,但是它也可以直接在瀏覽器中使用)。

Async 提供了大約20個函數(shù),包括 map, reduce, filter, forEach 等等,也有常用的異步流程控制模式,并行,瀑布等等。官方文檔里有詳細的說明,并且有實例,這里我們介紹一下兩個最常用的:parallel
、waterfall
。

parallel

并行執(zhí)行多個函數(shù),每個函數(shù)都是立即執(zhí)行,不需要等待其它函數(shù)先執(zhí)行。傳給最終callback的數(shù)組中的數(shù)據(jù)按照tasks中聲明的順序,而不是執(zhí)行完成的順序。

async.parallel([
    function(callback){
        setTimeout(function(){
            callback(null, 'one');
        }, 200);
    },
    function(callback){
        setTimeout(function(){
            callback(null, 'two');
        }, 100);
    }
],
// optional callback
function(err, results){
    // the results array will equal ['one','two'] even though
    // the second function had a shorter timeout.
});

parallel中的函數(shù)是并行的,沒有先后之分,callback中results參數(shù)的結(jié)果跟并行函數(shù)順序有關(guān)。上例中results值為['one', 'two']。

在本程序中,用戶注冊時,我們要校驗用戶名和郵箱是否被占用。分析一下:校驗用戶名校驗郵箱并有沒先后循序,可以并行校驗。我們只需要拿到校驗后的結(jié)果,做出處理即可。示例代碼如下:

async.parallel({
    username: function (callback) {
        User.findOne({username: user.username}, function (err, doc) {
            callback(null, doc);
        });
    },
    email: function (callback) {
        User.findOne({email: user.email}, function (err, doc) {
            callback(null, doc);
        });
    }
}, function (err, results) {
    if (results.username) {
        req.flash(config.constant.flash.error, '用戶名已被占用');
        res.redirect('/join');
        return;
    }
    if (results.email) {
        req.flash(config.constant.flash.error, '郵箱已被占用');
        res.redirect('/join');
        return;
    }

    user.password = utils.md5(user.password, 'base64');
    User.create(user, function (err, doc) {
        webHelper.reshook(err, next, function () {
            req.flash(config.constant.flash.success, '注冊成功,請登錄!');
            res.redirect('/login');
        });
    });
});

waterfall

按順序依次執(zhí)行一組函數(shù)。每個函數(shù)產(chǎn)生的值,都將傳給下一個函數(shù)。

waterfall跟parallel相反,是順序執(zhí)行一組函數(shù)。

async.waterfall([
    function(callback) {
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback) {
      // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    },
    function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'
});

第一個函數(shù)返回兩這值onetwo,由于waterfall是順序執(zhí)行的,所有等第一個函數(shù)執(zhí)行完,才會繼續(xù)執(zhí)行第二個函數(shù),并且onetwo傳遞給了第二個函數(shù),所以在第二個函數(shù)中arg1值為'one',arg2值為'two',然后通過callback,將three傳給了第三個函數(shù),所以第三個函數(shù)arg1值為'three',最后將'done'傳給了最后的回調(diào)函數(shù),所以result值為'done'。

那么在我們的程序中是怎么應用的呢?比如展示用戶詳情頁面中/u/username,我們需要展示用戶的基本信息,同時將此用戶的文章進行展示。前臺傳遞到后臺的參數(shù)是username,而我們只能通過userId才能查詢文章,所以我們需要先通過username查詢user,在通過user.id查詢此用戶的所有文章articles,然后將userarticles都傳到前臺,進行展示,代碼如下:

router.get('/:username', function (req, res, next) {
    var username = req.params.username;
    var User = dbHelper.User;
    var Article = dbHelper.Article;
    async.waterfall([
        function (callback) {
            User.findOne({username: username}).exec(function (err, user) {
                callback(null, user);
            });
        },
        function (user, callback) {
            if (user) {
                Article.find({_user: user.id}).populate('_user').exec(function (err, articles) {
                    callback(null, articles, user);
                });
            } else {
                callback(null, null);
            }
        }
    ], function (err, articles, user) {
        res.render('my', {
            articles: articles,
            user: user,
            menu: 'my'
        });
    });
});

總結(jié):async官方示例中說的很詳細了,它的功能非常強大,需要我們一個個將其摸索透。最終組織出漂亮的代碼出來。
官方文檔:https://github.com/caolan/async#asyncjs

添加自定義的404頁面

expressjs生成的代碼app.js中,默認404是當作500錯誤進行處理的,當我們請求到404后,會給出這樣一個錯誤頁面

404

而實際上404跟500是不一樣的,500是服務器端程序錯誤,404是很常見的一種資源不存在的錯誤,500能避免,但是404是不可避免的,所以我們需要有好的提示給用戶一個404頁面。改善方法如下:
app.js中找到catch 404 and forward to error handler對應的方法:

app.use(function (req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

問題就出在next(err),將err傳遞給下一個方法,也就是500那個。這里我們阻斷它繼續(xù)傳遞,直接渲染到前臺頁面:

app.use(function (req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    res.render('404');
});

然后在views下添加一個404.hbs,定制一下就ok!效果如下:

404

你可以自己訂制的更漂亮!

使用Handlebars模塊化你的頁面

已經(jīng)有一篇詳細的文章來單獨說明這個知識點,請移步:http://www.itdecent.cn/p/a38ec7ef339a

請關(guān)注專題:我的NodeJS學習之路(實踐之路)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,983評論 25 709
  • Node基本 node的最大特性莫過于基于事件驅(qū)動的非阻塞I/O模型。 node通過事件驅(qū)動的方式處理請求,無須為...
    AkaTBS閱讀 2,305評論 0 11
  • 22年12月更新:個人網(wǎng)站關(guān)停,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,393評論 22 257
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評論 19 139
  • 端午節(jié),幫著父親把家里認認真真打掃了一番,大大小小的家具也擦了一遍。 父親是一個極其干練的人,做事從不讓人吃虧。 ...
    南塵三閱讀 746評論 2 7

友情鏈接更多精彩內(nèi)容