在使用exoress 框架开发node项目是,异步调用往往是件很麻烦的事情,所以在闲暇之余就研究了下。。。
1、什么是promise?
Promise可能大家都不陌生,因为Promise规范已经出来好一段时间了,同时Promise也已经纳入了ES6,而且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现如今流行的类Promise类库相比少些API。
所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,然后A就可以在写计划的时候这么写:当B返回结果给我的时候,A执行方案S1,反之如果B因为什么原因没有给到A想要的结果,那么A执行应急方案S2,这样一来,所有的潜在风险都在A的可控范围之内了。
Promise规范如下:
一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
promise必须实现then
方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
2.promise原理分析
可以看到promise的规范并不是很多,下面我们一边分析promise一边自己写一个promise的实现。Promise实现的大致思路如下:
构造函数Promise接受一个函数function(resolve,reject)
,可以理解为传入一个异步任务,resolver接受的是成功时的回调,reject接受的是失败时的回调,这两参数和通过then传入的参数是对等的。
其次是then的实现,由于Promise要求then必须返回一个promise,所以在then调用的时候会新生成一个promise,挂在当前promise的_next上,同一个promise多次调用都只会返回之前生成的_next。
由于then方法接受的两个参数都是可选的,而且类型也没限制,可以是函数,也可以是一个具体的值,还可以是另一个promise。下面是then的具体实现:
例1 构建数据库连接的promise 对象: 见附件dbPool.js
例2
async function sendEBEserver(req, res) { var serverhost = req.body.service.trim(); var port = req.body.port.trim(); var parm = req.body.body.trim(); logger.info("sys", "begin query parm:" + parm); var method = req.body.method.trim() || "post"; var jwtparm = parseInt(req.body.ison); var jwtkey = req.body.string.trim(); var ul = req.body.url.trim(); var url = "http://" + serverhost + ":" + port + "/" + ul; logger.info("sys", "jwt password:" + jwtkey); var times = req.body.times.trim(); var time_start = new Date().getTime(); logger.info("receive param done!!!"); var ebereq = { url: url, method: (method ? method : 'POST'), json: false, timeout: 60 * 1000 * 10 }; if (parm) { logger.debug("sys original param:" + parm); var obj = {}; obj.gid = new Date().getTime() + "kaka"; obj.exp = moment().add(1, 'day').unix(); obj.content = parm; obj.areacode3 = ""; obj.areacode4 = ""; ebereq.json = false; logger.debug("sys before jwt:" + JSON.stringify(obj)); ebereq.body = parm; } logger.info("sys", "发送请求参数:" + JSON.stringify(ebereq)); var result = await new Promise(function(resolve,reject){ request(ebereq, function (err, response, body) { if (!err && response.statusCode == 200) { if (!ebereq.json && typeof body == "string") { body = JSON.parse(body); } logger.info("sys", "获取到" + url + ",返回的数据:" + JSON.stringify(body)); body.time = (new Date().getTime() - time_start) / 1000; body.times = times; resolve(body); } else { var error = (err ? err.message : (response.statusCode + ":" + response.statusMessage)); logger.error("sys", "获取到" + url + ",返回的错误:" + error); error.status = "请求出错"; error.time = (new Date().getTime() - time_start) / 1000; error.times = times; reject(error); } }); }) logger.debug("sys","request url: "+url+" finished..."); res.json(returnData.success(result)); }
通过构建promise对象,使request请求按照同步的方式返回。
上述资料来源:http://www.cnblogs.com/yunfeifei/p/4453690.html
其他:关于ES6的新特性,借鉴一个网址:http://www.cnblogs.com/Wayou/p/es6_new_features.html
1、通过第一部分可以知道要通过使用async/await处理异步逻辑,首先需要将异步过程包装为一个promise对象。注意在包装promise对象时resolve和reject参数。
2、 要在express项目中使用ES6的async/await关键字处理异步,在项目启动是需添加参数--harmony(此参数是是node支持部分ES6的新特性,但并不是完全支持,所以建议在使用时在此参数后显示ES6特点参数)
例如:可以通过node --harmony -async-await ./bin/www 的方式启动项目来支持 使用ES6的async/await关键字。
也可以通过pm2启动:pm2 start ./bin/www --node-args="--harmony-async-await"
在webstrom中也可通过配置启动:
注意:在webstrom使用ES6特性时默认编辑默认会async/await关键字报错,所以要修改编译器设置。