NodeJs回調操作Promise化


mongoose是一個NodeJs下MongoDB的ORM庫。使用這個庫,您從DB到表(collection)都不用創建了。只需要在項目中定義好Model

下面就是用上一篇的代碼來演示如何把mongoose的數據庫操作里的回調地獄(callback hell)輕松化解掉。

上一篇Petshop的代碼在這里

打開Promise的開關

mongoose已經開啟了對Promise的支持,只需要指定明確的Promise庫就可以:

var mongoose = require('mongoose'),
    Promise = require('bluebird');

本來每一個model的定義都需要引入mongoose庫,然后每次都給mongoose庫指定Promise庫太過冗繁。所以我們抽象代碼,在models目錄下創建一個base目錄,然后在里面添加一個index.js文件:

//petshop/server/models/index.js

var mongoose = require('mongoose'),
    Promise = require('bluebird');
    
mongoose.Promise = Promise;

module.exports = mongoose;

然后在model的定義都是用export的添加Promise的mongoose:

var mongoose    = require('./base'),
    bcrypt      = require('bcrypt-nodejs');

var Schema = mongoose.Schema;

var userSchema = new Schema({
    username: {type: String, unique: true, required: true},
    password: {type: String, required: true}
});

...

module.exports = mongoose.model('User', userSchema);

這樣,使用了base目錄下的mongoose定義的model都具備了Promise的能力。

在調用查找更新等方法的時候只需要這樣:

    User.findOne({ username: username }).exec().then(function (u) {
        if (!u) {
            done(null, false);
            return;
        }
        var verifyPasswordAsync = Promise.promisify(u.verifyPassword, { context: u });
        verifyPasswordAsync(password).then(function (match) {
            console.log('password match ' + match);
            if (!match) {
                console.log('is match ' + match);
                done(null, false);
            } else {
                done(null, u);
            }
        });
    }).catch(function (err) {
        done(err);
    });

解釋如下:
第一行代碼User.findOne({ username: username }).exec()在exec調用之后就返回了一個Promise。后面就可以使用Promise的then方法來開始Promise的方式依次調用和異常處理了。

單獨promise化一個方法

在mongoose內置的Promise支持不能完成某些方法的時候還可以另外使用bluebird庫來單獨的針對這個方法來使其promise化。比如上例的u.verifyPassword代碼:

userSchema.methods.verifyPassword = function (password, callback) {
    bcrypt.compare(password, this.password, function (err, match) {
        if (err) {
            return callback(err);
        }

        callback(null, match);
    });
};

單獨的promise化verifyPassword方法:

var verifyPasswordAsync = Promise.promisify(u.verifyPassword, { context: u });

之后的使用:

verifyPasswordAsync(password).then(function (match) {
    console.log('password match ' + match);
    if (!match) {
        console.log('is match ' + match);
        done(null, false);
    } else {
        done(null, u);
    }
});

Promise化的一般原則

對於上面例子這里稍作引申。Promise化的時候使用的是bluebird庫。

下面使用的例子代碼如下:

function Dog(name) {
    this.name = !name ? 'Tiger': name;
}

Dog.prototype.bite = function(target, cb){
    console.log(this.name + ' bite ' + target);
    cb(null, target);
};

Promise化一個對象

Promise化一個對象使用promisifyAll方法。

var Promise = require('bluebird');

var d = Promise.promisifyAll(new Dog());
d.biteAsync('hellokitty');

輸出:

Tiger bite hellokitty

注意:Promise化之后調用方法需要加上Async后綴。bite=>biteAsync

Promise化一個方法

Promise化的是一個帶有回調的方法。這個Promise返回的結果就是回調正確的情況下獲得的值。

var someDog = new Dog("small");
var otherDog = new Dog("big");
var proDog = Promise.promisify(someDog.bite, {context: otherDog});
proDog('YOU').then(function(target) {
    console.log('then ' + target);
    walk();
})

在Promise話一個方法的時候需要考慮是不是指定context。上例中如果不指定context的時候會報錯。一般,如果是require引入的庫的方法不需要指定context,但是局部變量需要制定。指定context以后,方法的this指向的上下文就是這個context對象。

總結

Promise化之后,回調地獄的問題就有很好的解決了。不過,還需要考慮項目的大小和回調的深度來決定是否要Promise化。畢竟Promise會增加一定的代碼量,也有一定的學習曲線。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM