我们在很多使用Promise的时候都会有如下的用法1
2
3
4
5
6MongoClient.connect()
.then(function (db) {
var col = db.collection('something');
col.find().toArray()
.then(console.log, console.error);
});
真正的连接对象在connect()
之后才返回,只能在then
回调中处理后续的数据库操作,而如果想复用这个连接,就需要在外面用另一个对象保持对它的引用,代码可能就会变成这个样子1
2
3
4
5
6
7var db;
MongoClient.connect()
.then(function (_db) {
db = _db;
});
//别的地方
db.collection('otherthing')....
这样的缺点是在连接未建立时访问db
会引发异常,当然最正确的做法应该是1
2
3
4
5var db = MongoClient.connect();
//别的地方
db.then(function (_db) {
_db.collection('otherthing')....
});
可以保持对一个连接的复用,但这样还是嵌了一层回调。
在之前用Proxy
写了几个玩具之后,有个念头渐渐浮上心头,Proxy
也许可以改变这个模式,我们可以用Proxy
预先收集好调用链,然后再将其内部转为Promise
的调用链,简单来说思路如下:
先将原始数据用
Promise.resolve
包裹,使其成为一个Promise
对象,并返回该对象的代理Proxy
对该
Proxy
的get
和apply
操作实际上就是在后面多加一个.then
回调,并会继续返回Proxy
,以实现链式调用当将
Proxy
作为函数运行时对参数预先做Promise.resolve
处理,使得它可以接受Promise
参数在内部的取值、调用时做错误捕获,使得在冗长的调用链中捕获错误成为可能
最终的值从
.then中
获取,Promise
的特性保证了最终值的可达性
多说无用,以下是一些示例:
1 | var request = require('request-promise'); |
首先代理了JSON.parse
使其可以接受Promise
类型的参数,并挂上长长的一串取属性操作,得到的结果最后通过console.log
打印出来
当试图从undefined
中读取属性时,会返回类似这样的错误1
TypeError: can't read property 'textRaw' in {Promixy}(1 args).miscs.10, it got a undefined
虽然有些不直观但好歹调用链是能看出来了1
2_(Promise.resolve(_(_(_(111))))).then(_(console.log), _(console.error));
//111
即使嵌了很多层,这个Proxy
依然是Promise
的代理,所以最后还是可以得到正确的结果的
而刚才的mongo连接也可以简化为
1 | var db = _(MongoClient.connect()); |
实质上是Proxy
在内部代替用户嵌了一层.then
,代码上更加简单直观,且从它上面取的所有属性依然有Proxy
包裹,所以你甚至可以实现这样变态的调用方式
1 | db.collection('something').find().toArray()[0].somekey |
直接取出集合第一条中的某个值,当然我不建议这种很难理解的写法
综上,这个模块还有许多待发现的可用之处,我个人也将它用于了生产环境中,npm地址,如果你有任何建议或发现了bug,欢迎到github反馈
2017-07-04补充
Node7.6之后的版本原生支持async/await
,建议直接使用这个特性