美文网首页
callback到promise到async await的演变

callback到promise到async await的演变

作者: Eason_0cce | 来源:发表于2019-08-13 13:08 被阅读0次

    前记:本篇文章不是基础知识科普篇,而是实际工作中的记录篇,以实际工作需求带出前端请求处理的演变历程。

    开篇先来一个回调callback的例子(来自微信小程序 Api):

    wx.request({
      url: 'test.php', //仅为示例,并非真实的接口地址
      data: {
        x: '',
        y: ''
      },
      header: {
        'content-type': 'application/json' // 默认值
      },
      success (res) {
        console.log(res.data)
      }
    })
    

    赤裸裸的success回调,设想一下如果我request里面嵌request甚至再嵌好几个~~~那画面就叫回调炼狱。

    改造成promise形态,学习最简单的promise方法封装

    再不改造更待何时,第一个浮现在脑海的东西自然是promise,我们先来改造request,当然我改造了不少小程序原有的api。我给这些原有api都采用了promise化的赋能。在wx方法里面独立出来了wx.top库。于是我的request变成了wx.top.request,使用参数不变。

    wx.top = {};
    wx.top.request = function(){
        return new Promise((resolve, reject){
            wx.request({
              url: args.url, //仅为示例,并非真实的接口地址
              data: args.data,
              //header:这里你按需定制比如全局token
              success (res) {
                //这里也可以根据后端通讯规范,设置自己的relove和reject
                relove(res);
              },
              fail (res){
                reject(res);
              }
            });
        });
    }
    

    此时的request数据返回怎么处理

    wx.top.request({
      url : "xxx",
      data : {}
    }).then(res=>{
      console.log(res); //请求的返回信息
    });
    

    以上是promise最直接的用法,封装方式就是return new Promise对象即可。Promise化的方法,你只需要关注两点,什么时候resolve什么时候reject。

    在我们项目上线后的一个月,突然有一天我接到一个工单,说是直接下单不能买了,跟后端反复查实,最后找到了问题,原来是下单数据依赖的接口请求有好几处(拉取地址信息,计算运费,计算个人账户余额),很容易出现还没算好运费就被用户点了购买的情况,我试图去用三个标记去判断可以提交,拦截是可以,代码可读性不好,然后又没办法做友好度提示。于是我又做了如下改进:

    利用promise封装原子方法,利用promise实现接口以来,利用promise实现统一状态管理

    一、计算运费方法:

    calcPostFee(){ //获取运费
        return new Promise((resolve, reject) => {
            if(isPost || !address){ //如果包邮或者没有地址信息返回0
                resolve(0);
            }else{
                orderFreight(address, order).then(res => { //order
                    //这里resolve(运费)或者reject(错误信息)       
                }).catch((err) => {
                  console.log(err); //接收错误信息-信息包含请求错误|代码错误
                  wx.top.showTip(getObjVal(err, ['data', 'msg'],true) || '获取运费失败'); //getObjVal是我自己封装的js对象数据方法
                  reject(err);
                });
            }
        });
      },
    

    二、获取地址信息:

     queryAddress(){ //获取运费
        return new Promise((resolve, reject) => {
          queryUserAllAddress({ header : {token : this.userInfo.token} }).then(res=>{
            //这里resolve(地址信息)或者reject(错误信息);
          }).catch((err)=>{
            console.log(err);
            wx.top.showTip(getObjVal(err, ['data', 'msg'],true) || '获取地址失败');
            reject(err);
          });
        });
      },
    

    三、计算个人余额及订单费用方法:

     calcOrder(order){ //计算余额及订单费用
        return new Promise((resolve, reject) => {
            //单纯的计算没有任何请求,这里我也想提醒大家一下promise是个工具,不是只有请求才可以用,万物皆可promise
            //这里resolve(正确信息)或者reject(错误信息);
        });
      },
    

    四、接口总统领:

    prepareData(){
      return new Promise((resolve, reject)=>{
        wx.showLoading({title : "订单正在生成...", mask : true}); //开启mask,不可点击。可以统一提示了,代码再也不散乱了
        this.queryAddress().then(()=>{ //地址信息回来之后
          return this.calcPostFee(); //再计算运费
        }).then(()=>{
          return this.calcOrder(); //算好订单数据之后
        }).then(()=>{
            reeolve();
            wx.hideLoading();//解开遮罩,避免误点
        }).catch((err)=>{
          wx.hideLoading(); //隐藏loading
        });
      });
    }
    
    prepareData.then(res=>{
      this.setData({ disableBtn : false; }); //可以去购买了
    }).catch(()=>{
            this.setData({ disableBtn : true; });//设置购买按钮不可点击
    })
    

    下面我开始推出async await

    这是https://javascript.info/async-await中的一句话,概括的很到位,
    说async await是让promise以更舒适更时髦的方式工作的语法,而且其极度容易理解和使用

    image.png

    先上个例子来让大家走进基本语法:

    async prepareData(){
      wx.showLoading({title : "订单正在生成...", mask : true});
      try{ 
        let address = await  this.queryAddress();
        let fee = await  this.calcPostFee();
        let amt = await  this.calcOrder();
        wx.hideLoading();
        return true;
      }catch(e){//任意一个方法错误(reject或者js错误)都可以被捕获
        wx.hideLoading();
      }
    }
    prepareData().then(...)
    

    嗯,不错确实优雅了很多,也洋气了,await用的很传神。就是捕获错误的方式有点不优雅,好的,上另外一种方式:

    async prepareData(){
        let address = await  this.queryAddress();
        let fee = await  this.calcPostFee();
        let amt = await  this.calcOrder();
    }
    
    //使用
    wx.showLoading({title : "订单正在生成...", mask : true});
    prepareData().then(...).catch(...);
    

    咿?这里的prepareData也可以then跟catch?
    没错儿,async 修饰词就是让方法体变成了Promise对象,等同于在方法体内部return new Promise。
    而await只能在async修饰的方法体内部使用,作用也很明确,我去买橘子,你等着。
    很多人说async await方法是回调的终极解决方案,我不认同,我认为其只是一种方法载体,让Promise的写法更优雅。 请看如下实验,fa方法执行时间很明显比fb执行时间要长,但是await告诉fb你先等着,请看下例:

    验证一下await的执行顺序

    function fa(){
        return new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve('a')
            },2000);
        });
    }
    function fb(a){
        return new Promise((resolve, reject)=>{
            setTimeout(()=>{
                console.log(a);
                resolve('b'+a)
            },1000);
        });
    }
    async function c(){
        let a = await fa();
        let b = await fb(a);
    }
    c().then(res=>{
        console.log(res);
    });
    

    反思Promise有all跟race方法,怎么用async跟await去实现这样的机制呢?也许有方式,但是我想说的还是它只是Promise的载体,跟最终的解决方案范畴有点出入。不管js以什么样的方式什么样的速度发展,我希望能够出来一些解决真正业务痛点的语法,谢谢各位观看者。

    相关文章

      网友评论

          本文标题:callback到promise到async await的演变

          本文链接:https://www.haomeiwen.com/subject/jrcvjctx.html