进化!将nodejs代码重构为ES6风格
2017-09-08 14:33
246 查看
进化!将nodejs代码重构为ES6风格
作为零后端基础直接去做node后端的小白,曾经纠结了很长一段时间去理解node的异步以及代码风格,在2016年最终使用了传统的es5和callback作为编码风格和处理异步的方法。对于callback的回调地狱,则选择async.js来处理。但是面对复杂的逻辑,缩进依然让轻微代码洁癖者感到恶心。node8的出现突然警醒了我,不能再这么堕落了!虽然稳定版本还在node6,但是依然可以利用promise或async/await重构代码!于是开始了简化代码的尝试。注:笔者特别菜,如在观看此文时有何指正,请不吝赐教
callback
线上一段恶心的代码,不用细读,就是恶心一下这里用了async.waterfall、async.each处理异步,request和自己简单封装的postgresql操作数据库。
router.get('/account',function (req,res,next) { var userid=req.query.userid; var date=moment().format('YYYY-MM-DD'); async.waterfall([ function (callback) { postgresql.getYsClient(function (getClient,done) { var client=getClient; var sql=util.format("SELECT ta.id,ta.appid,ta.account_type as type,ta.name,ta.account_status " + "FROM tb_third_account ta,tb_user_and_third_account uata " + "WHERE ta.id=uata.account_id and ta.account_type=110102 and uata.user_id=%s",userid); client.query(sql,function (err,results) { done(); if(err){ return callback(err); } callback(null,results.rows); }) }) },function (list,callback) { async.each(list,function (item,cb) { var url=urls_api.getFeedAccountReport(item.appid,date,date); var options={ method:'GET', url:url, json:true } request(options,function (error,response,results) { if(!error&&response.statusCode===200){ < 4000 span class="hljs-keyword">if(results.result==='failed'||results.data.length===0){ console.log(options) return cb() } item.view=results.data[0].kpis[0]; item.click=results.data[0].kpis[1]; item.cost=results.data[0].kpis[2]; item.cpc=results.data[0].kpis[3]; item.ctr=results.data[0].kpis[4]; item.cpm=results.data[0].kpis[5]; cb(); }else { cb(); } }) },function (err) { callback(null,list); }) } ],function (err,list) { res_util.back_json(res,list,'success',null); }) });
现在我自己看这段代码都感觉不忍直视。
如果要重构成Promise风格的代码,首先要把我对sql的封装和request改成Promise函数。那么先从sql的封装开始。
co+yield
我在/modules/postgresql 里是这么写的首先我创建了一个连接池pool_msdb这里写法就不多说了,pg文档里就有,client是获取的链接,done方法是要在查询结束后将链接放回连接池,如果不done掉的话,这条链接将一直被占用。多次使用这个方法,连接池将无链接可用。
var getClient = function(callback){ pool_msdb.connect(function (err,client,done) { if(err){ return console.error('error fetching client from pool', err); } callback(client,done); }); }
这样封装的话,外部使用的话就要这么写
postgresql.getYsClient(function (getClient,done) { var client=getClient; var sql=''; client.query(sql,function (err,results) { done(); if(err){ return callback(err); } callback(null,results.rows); }) })
好麻烦,将封装改写成
var msdbClientExecute=function (sql,param) { return new Promise(function (resolve,reject) { pool_msdb.connect(function (err,client,done) { if(err){ return console.error('error fetching client from pool', err); } client.query(sql,param,function (error,results) { done(); if(error){ console.log(sql) return reject(error); } resolve(results.rows); }) }); }) }
client.query(sql,param,callback)
这种写法比拼sql语句好在可以防止注入式攻击。也是pg文档里的,可见我当时写代码多么随意。。。
这个方法直接传入sql语句和参数,返回一个promise,promise成功的返回值就是查询结果。这里自己去拿一个链接,查询后直接done掉。避免了写代码时忘了done掉非常尴尬。
那么在调用时,就可以这么写
let sql="select * from table where id=$1" let list =yield postgresql.ysClientExecute(sql,[userid]);
爽不爽!???
还有个request,request并不支持返回一个promise,那么我们在github上随随便便找一个star比较高的轮子,request-promise
const request_p=require('request-promise');
写起来炒鸡简单
var results=yield request_p(options);
那么下一步,就是用yield和co配合搞搞事情。
router.get('/account',function (req,res,next) { let userid=req.query.userid; let date=moment().format('YYYY-MM-DD'); co(function* () { let sql="selext * from table where userid=$1"; let list =yield (postgresql.ysClientExecute(sql,[userid])); for(let item of list){ let url=urls_api.getFeedAccountReport(item.appid,date,date); let options={ method:'GET', url:url, json:true } results=yield (request_p(options)); if(results.data&&results.data.length>0&&results.data[0].kpis){ item.view=results.data[0].kpis[0]; item.click=results.data[0].kpis[1]; item.cost=results.data[0].kpis[2]; item.cpc=results.data[0].kpis[3]; item.ctr=results.data[0].kpis[4]; item.cpm=results.data[0].kpis[5]; } } res_util.back_json(res,list,'success',null); }) });
这里用co作为执行器,在co这个代码块里,yield后面跟一个Pomise对象。yield会同步执行这个Promise。
async/await
co+yield虽然很爽。但是并不是我的追求。不是说官方亲儿子是async/await吗。
我的版本还停留在node6,查了一下可以用bable去支持,但是我看着感觉麻烦(懒癌,该改)。有一个我觉得比较简单的方法,就是asyncawait这个库,star挺多看来用的人不少。引入这个库后,async/await的操作和原生唯一的不同在于需要将后面的函数加个括号。我觉得既能快速体验async/await,以后改成原生也好改,当然要试试了。
const async1=require('asyncawait/async'); const await=require('asyncawait/await');
那么代码就变成
router.get('/account',function (req,res,next) { let {userid}=req.query;//es6写法,解构赋值 let date=moment().format('YYYY-MM-DD'); (async1(function () { let sql="selec * from tabel where userid=$1"; let list =await (postgresql.ysClientExecute(sql,[userid])); for(let item of list){ let url=urls_api.getFeedAccountReport(item.appid,date,date); let options={ method:'GET', url:url, json:true } let results=''; results=await (request_p(options)); if(results.data&&results.data.length>0&&results.data[0].kpis){ item.view=results.data[0].kpis[0]; item.click=results.data[0].kpis[1]; item.cost=results.data[0].kpis[2]; item.cpc=results.data[0].kpis[3]; item.ctr=results.data[0].kpis[4]; item.cpm=results.data[0].kpis[5]; } } res_util.back_json(res,list,'success',null); }))().catch(e=>{ res_util.back_json(res,null,'failed',e); }) });
这个catch简直是神来之笔,
简直了
原来觉得写try/catch好麻烦,一行缩进啊!现在完美解决
还有谁!
相关文章推荐
- 翻译《有关编程、重构及其他的终极问题?》——14.一个好的编译器和代码风格还不够
- switch代码的进化 重构,至策略模式 工厂模式 插件模式 表驱动
- 使用ES6语法重构React代码
- 2014.02.13 重构代码之注释习惯和风格
- 将Java代码重构为Java8 Stream 风格三则示例
- 使用nodejs创建一个网站-重构代码
- 使用ES6语法重构React代码详解
- React使用ES6语法重构组件代码
- NodeJs:Happy代码生成器,重构了代码,更新了文档,完善了示例,欢迎下载使用
- Felix的Nodejs代码风格
- 一段实现选择框javascript代码的重构
- 关于合理使用SpringMVC统一异常处理机制以改善代码风格的一些思考
- [转] Linux内核代码风格 CodingStyle [CH]
- 代码重构学习笔记三:重构72招式
- 重构-改善既有代码的设计:重构原则(二)
- 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)
- Linux内核代码风格
- 代码的坏味道之二——译自《重构》
- 代码风格
- 《重构 改善既有代码的设计》读书笔记2