美文网首页
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