在NodeJS
环境下使用MongoDB
时,作为数据库管理工具的Mongoose
已经成为项目开发的标配,它丰富了Mongo的原生API,为开发者提供了扩展接口,同时以 schema-based 的方式,来操作Mongo
在开发Node时读写数据库难免要进行频繁且深层次的回调操作好在Mongoose的读写操作都有方法以 promise
对象返回,基于这点,结合其它三方Promise类库,可以避免多重嵌套的发生。(ES6 Generator 能更好的控制异步流程,结合Promise可以更好的解决 Callback Hell)
Talk is cheap. Show me the code
先建立我们的数据模型 —— Family
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var mongoose = require('mongoose'); var Schema = mongoose.Schema; var FamilySchema = new mongoose.Schema({ generation: { type: Number, default: 1 }, sex: { type: String, default: 'male' }, name: { type: String, default: '' } });
var Family = mongoose.model('Family', FamilySchema);
|
Mongsoose
的Model层API 大致分为:创建(Create)、更新(Update)、读取(Find)和删除(Remove)四类,create 和 remove 操作返回 promise
对象,update 和 find 操作返回 query 对象,需要注意的是,使用 find前缀 方法(如:Model.findOneAndRemove、Model.findByIdAndRemove、Model.findByIdAndUpdate)时返回对象均为 query
对于 query
对象,只需要对其调用 exec
方法,就可以返回 promise
对象了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| * Query */ Family.findOne({name: '转二'}).exec();
* Update */ Family.findByIdAndUpdate(id, {$set: { name: '转二', generation: 3 }}).exec();
* Create */ var array = [{ generation: 4, sex: female, name: '虫虫' }, { generation: 3, sex: female, name: '芮芮' }];
Family.create(array);
* Delete */
Family.remove({ name: '转二' });
|
有了 promise
返回,异步梦魇已经解决一半了
在 promise
类库中我比较喜欢Q
,相比于其他同类工具,Q比较简单,而且和 Angular
的 $q
和 jQuery
的 Deferred
语法相似。不过还是推荐使用 es6-promise,作为 polyfill 可以让代码在未来无缝衔接 es6
为了举例子,现在创建第二个数据模型 —— Company
1 2 3 4 5 6 7 8 9 10
| var mongoose = require('mongoose'); var Schema = mongoose.Schema;
var CompanyMemberSchema = new mongoose.Schema({ empName: String, empId: String, sex: String });
var CompanyMember = mongoose.model('CompanyMember', CompanyMemberSchema);
|
我们想要从一个 Family
中通过 _id
找到一个家庭成员,通过这个成员的名字,在某个确定的公司里查找此人,然后把此人从公司名单中移除
Without promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var id = "1237213921936129";
Family.find({_id: id}, function(err, persons) { CompanyMember.findOne({ 'empName': persons[0]['name'] }, function(err, person) { CompanyMember.remove({ 'empId': person.empId }, function(err, person) { if(err) { return console.error(err); } console.log('Remove' + person + 'successfully!'); }) }) });
|
With promise (Q)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| var id = "1237213921936129"; Q.fcall(function() {
return Family.findOne({ _id: id }).exec(); }).then(function(person) { return CompanyMember.findOne({ empName: person['name'] }).exec(); }).then(function(person) {
return Company.remove({empId: person['empId']}); }).then(function(person) {
console.log('Remove' + person + 'successfully!');
}).catch(function (err) { return console.error(err);
}).done();
|
至于并发控制,Q
也有解决方案:
1 2 3 4 5 6
| Q.spread([promiseA, promiseB], function(A_value, B_value) { console.log('A_value': A_value); console.log('B_value': B_value); }, funcion(err) { return console.error(err); });
|
Q的使用查阅 http://documentup.com/kriskowal/q/
Mongoose的使用查阅 http://mongoosejs.com/docs/guide.html
下一篇会聊一聊 Generator
+ Promise
组合来解决 Mongoose 的异步回调问题
本文地址:
https://mrpeak.github.io/2015/03/20/mongoose-promise/