mongoose
是一個NodeJs下MongoDB的ORM庫。使用這個庫,您從DB到表(collection)都不用建立了。只須要在項目中定義好Model
。javascript
下面就是用上一篇的代碼來演示如何把mongoose的數據庫操做裏的回調地獄(callback hell)輕鬆化解掉。java
上一篇Petshop的代碼在這裏。node
mongoose
已經開啓了對Promise的支持,只須要指定明確的Promise庫就能夠:git
var mongoose = require('mongoose'), Promise = require('bluebird');
原本每個model的定義都須要引入mongoose庫,而後每次都給mongoose庫指定Promise庫太過冗繁。因此咱們抽象代碼,在models目錄下建立一個base目錄,而後在裏面添加一個index.js文件:github
//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的能力。promise
在調用查找更新等方法的時候只須要這樣:mongoose
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的方式依次調用和異常處理了。學習
在mongoose內置的Promise支持不能完成某些方法的時候還能夠另外使用bluebird庫來單獨的針對這個方法來使其promise化。好比上例的u.verifyPassword
代碼:ui
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化的時候使用的是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化一個對象使用promisifyAll
方法。
var Promise = require('bluebird'); var d = Promise.promisifyAll(new Dog()); d.biteAsync('hellokitty');
輸出:
Tiger bite hellokitty
注意:Promise化以後調用方法須要加上Async
後綴。bite
=>biteAsync
。
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會增長必定的代碼量,也有必定的學習曲線。