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. How to Solve Lonsdor K518ISE Abnormal Display by Factory Resetting

    Here’s the working solution to Lonsdor K518ISE Key Programmer abnormal display after upgrade. Proble ...

  2. 来看看N多设计师笔下的Spider Man

    很多电影在大获成功后,其中的人物都成了火热的IP,漫威的各类超级英雄就是个很好的例子,今天突发奇想看看各种漫画人物在插画师手中同一人物会有什么样的不同,所以以蜘蛛侠为主题,搜集了很多插画师笔下的蜘蛛侠 ...

  3. Spring 循环引用(二)源码分析

    Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环引用相关文章: & ...

  4. jqgrid子表格

    .前台 <%-- builed by manage.aspx.cmt [ver:] at // :: --%> <%@ Page Language="C#" Au ...

  5. 模板 Template

    package ${enclosing_package}; import java.io.IOException;import javax.servlet.ServletException;impor ...

  6. openssl pem密钥文件rsa加密解密例子

    准备工作 命令行加密解密,用与比对代码中的算法和命令行的算法是否一致 C:\openssl_test>openssl rsautl -encrypt -in data.txt -inkey pu ...

  7. Python : locals and globals

    Python有两个内置的函数,locals() 和globals(),它们提供了基于字典的访问局部和全局变量的方式.Python使用叫做名字空间的东西来记录变量的轨迹.名字空间只是一个 字典,它的键字 ...

  8. MyEclipse Server view报错解决方法

    MyEclipse Server view报错解决方法 方法/步骤     启动MyEclipse,弹出一个框,报错. ---------------------------------------- ...

  9. 基于Web Service的客户端框架搭建一:C#使用Http Post方式传递Json数据字符串调用Web Service

    引言 前段时间一直在做一个ERP系统,随着系统功能的完善,客户端(CS模式)变得越来越臃肿.现在想将业务逻辑层以下部分和界面层分离,使用Web Service来做.由于C#中通过直接添加引用的方来调用 ...

  10. WPF禁止拖拽窗口到边缘自动最大化

    近期有个需求,可以通过拖拽改变窗口大小,但是不允许窗口最大化.最小化.拖到边缘的时候也不能自动最大化. 要想禁止拖拽窗口到边缘自动最大化,只要改注册表即可,但是系统所有应用都会被禁止. 1.运行reg ...