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

NodeJs回调操作Promise化的更多相关文章

  1. 回调、Promise、async-await

    第一章 异步:现在与将来 程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心. 场景:等待用户输入.从数据库或文件系统中请求数据.通过网络 发送数据并等待响应,或者是在以固定时间间隔执行 ...

  2. 【笔记】HybridApp中使用Promise化的JS-Bridge

    背景: HybridApp,前端采用JS-bridge的方式调用Native的接口,如获取设备信息.拍照.人脸识别等 前端封装了调用库,每次调用Native接口,需要进行两步操作(1.在window下 ...

  3. 【javascript】异步编年史,从“纯回调”到Promise

    异步和分块——程序的分块执行   一开始学习javascript的时候, 我对异步的概念一脸懵逼, 因为当时百度了很多文章,但很多各种文章不负责任的把笼统的描述混杂在一起,让我对这个 JS中的重要概念 ...

  4. NodeJs连接操作MongoDB数据库

    NodeJs连接操作MongoDB数据库 一,介绍 MongoDB是一种文档导向数据库管理系统,由C++撰写而成.介绍如何使用 Node.js 来连接 MongoDB,并对数据库进行操作. Mongo ...

  5. nodeJS文件操作

    让前端觉得如获神器的不是NodeJS能做网络编程,而是NodeJS能够操作文件.小至文件查找,大至代码编译,几乎没有一个前端工具不操作文件.换个角度讲,几乎也只需要一些数据处理逻辑,再加上一些文件操作 ...

  6. 在nodeJS中操作文件系统(二)

    在nodeJS中操作文件系统(二)   1. 移动文件或目录 在fs模块中,可以使用rename方法移动文件或目录,使用方法如下:     fs.rename(oldPath,newPath,call ...

  7. 回调与Promise

    Promise 对象就是用于表示一个异步操作的最终状态(成功或失败).它的流程就是在什么状态下需要执行什么样的操作. resolve简单理解就是一步操作执行成功后的回调函数 then是Promise对 ...

  8. [WCF编程]10.操作:回调操作

    一.回调操作概述 WCF支持服务将调用返回给它的客户端.在回调期间,许多方面都将颠倒过来:服务将成为客户端,客户端将编程服务.回调操作可以用在各种场景和应用程序中,但在涉及事件或者服务发生时间需要通知 ...

  9. 重温WCF之数单向通讯、双向通讯、回调操作(五)

    一.单向通讯单向操作不等同于异步操作,单向操作只是在发出调用的瞬间阻塞客户端,但如果发出多个单向调用,WCF会将请求调用放入到服务器端的队列中,并在某个时间进行执行.队列的存储个数有限,一旦发出的调用 ...

随机推荐

  1. node.js中npm包管理工具

    现在安装node.js,默认就会帮我们装上了npm包管理工具,npm主要用来下载,安装,管理第三方模块. 创建一个包描述文件: npm init [-y] 查看包的信息 npm info <pa ...

  2. 当时钟事件声明为过程变量 让system.threading.timer时钟失效

    这个项目的小模块就是画label 控件到tablepayoutpanel表单 之中, 中间用到了时钟,事件(带返回值的),哈希表 .由于时钟定义在 form1的启动构造函数中导致了form1,启动完毕 ...

  3. 5B - 一只小蜜蜂...

    有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行.请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数. 其中,蜂房的结构如下所示.  Input 输入数据的第一行是一个整数N,表示测试实例的个数,然 ...

  4. PAT 1088 三人行(20 分)(暴力破解+流程分析)

    1088 三人行(20 分) 子曰:"三人行,必有我师焉.择其善者而从之,其不善者而改之." 本题给定甲.乙.丙三个人的能力值关系为:甲的能力值确定是 2 位正整数:把甲的能力值的 ...

  5. 显示实现接口的好处c#比java好的地方

    所谓Go语言式的接口,就是不用显示声明类型T实现了接口I,只要类型T的公开方法完全满足接口I的要求,就可以把类型T的对象用在需要接口I的地方.这种做法的学名叫做Structural Typing,有人 ...

  6. 网络编程 tcp(一)

    server端: #include <stdio.h> #include <string.h> #include <unistd.h> #include <s ...

  7. 大神你好,可以帮我P张图吗?

    韩国版的求大神帮我P张图,看得有点下巴脱臼啊!哈哈哈哈哈哈哈~ 感觉照片拍得很尴尬,请大神P得更有动感 拍了跳跃照片,但内衣露出来一点,能帮忙去掉吗 不喜欢没穿制服的样子,请帮忙加上制服 ​ 希望背景 ...

  8. Null value was assigned to a property of primitive type setter of cn.itcast.oa.domain.Forum.topicCount

    [引用http://m.blog.csdn.net/blog/u013998070/41087351] Null value was assigned to a property of primiti ...

  9. powershell上传证书

    https://www.cnblogs.com/threestone/p/4001632.html powershell上传证书

  10. jquery单行文字上下循环滚动

    html代码: <div class="box"> <div class="t_news"> <b>已关联奖励账号.昵称:& ...