进化!将nodejs代码重构为ES6风格

进化!将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){

                        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好麻烦,一行缩进啊!现在完美解决
还有谁!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值