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. springboot 项目添加jaeger调用链监控

    1.添加maven依赖<dependency> <groupId>io.opentracing.contrib</groupId> <artifactId&g ...

  2. php 类与对象

    1.类与对象 对象:实际存在该类事物中每个实物的个体.$a =new User(); 实例化后的$a引用:PHP的别名,两个不同的变量名字指向相同的内容 封装: 把对象的属性和方法组织在一个类(逻辑单 ...

  3. sqlalchemy根据数据库结构生成映射的实体

    # !/usr/bin/python # -*- coding: UTF-8 -*- from sqlalchemy import * from sqlalchemy.orm import sessi ...

  4. 更精确的判断对象类型js方法

    function isArray(o) { return Object.prototype.toString.call(o) === '[object Array]'; } function isRe ...

  5. System.load()与System.loadLibrary()

    Java load 和 loadlibrary方法的区别 1.  相同点: 两个方法都是用来装载dll文件,不论是JNI库文件还是非JNI库文件.本地方法在被调用时都需要通过这两发方法之一将其加载至内 ...

  6. unity3d的执行顺序

  7. ubuntu安装rubyOnRails

    https://gorails.com/setup/ubuntu/16.04#ruby-rbenv 文章很详细

  8. idea如何搭建springboot框架

    首先简单介绍下Spring Boot,来自度娘百科:Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进 ...

  9. 2018.10.25 bzo1227: [SDOI2009]虔诚的墓主人(组合数学+扫描线+bit)

    传送门 有点难调啊.其实是我自己sb了 不过交上去1A1A1A还是平衡了一下心态. 所以这道题怎么做呢? 我们考虑对于一个点(x,y)(x,y)(x,y)如果这个点成为中心,正左/右/上/下分别有l/ ...

  10. 关于Cell中的各种值的类型判断

    switch (cell.getCellType()){ case Cell.CELL_TYPE_NUMERIC: //数字 cellValue = stringDateProcess(cell); ...