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会增长必定的代码量,也有必定的学习曲线。