ES6与ES7异步处理

在ES6中Promise()异步函数最突出的特点就是可以使用.then()来替换之前回调函数的多层嵌套 增加代码可读性和维护性

如:runAsync1().then(function(data){

console.log(data);

return runAsync2();}).then(function(data){

console.log(data);

return runAsync3();}).then(function(data){

console.log(data);});


具体的异步函数!

function runAsync1(){

var p = new Promise(function(resolve, reject){ //做一些异步操作

setTimeout(function(){

console.log('异步任务1执行完成');

resolve('随便什么数据1');

}, 1000);

});

return p;

}

function runAsync2(){

var p = new Promise(function(resolve, reject){ //做一些异步操作

setTimeout(function(){

console.log('异步任务2执行完成');

resolve('随便什么数据2');

}, 2000);

});

return p;

}

function runAsync3(){

var p = new Promise(function(resolve, reject){ //做一些异步操作

setTimeout(function(){

console.log('异步任务3执行完成');

resolve('随便什么数据3');

}, 2000);

});

return p;

}



第二个例子:
  function loading(src) {
    return new Promise((resolve, reject) => {//返回Promise实例对象
      let img = document.createElement("img");
      img.src = src;
      img.onload = function () {
        resolve(img);//利用resolve将img对象导出去做.then()函数的参数
      };
      img.onerror = function (err) {
        reject(err)
     }
   })
 }
 
 function showimg(imgs) {//回调函数
   imgs.forEach(function (img) {
     document.body.appendChild(img);
   });
 }
 Promise.all([
   loading("./img/banner01.jpg"),
   loading("./img/banner02.jpg")
 ]).then(showimg);
这里的意思是先执行loading() 然后再执行 showimg函数 参数来源于resolve导出


最近在项目中有这么一个需求,当用户使用积分去兑换某个商品之后,点击某个按钮会调用两个接口,其中先调用一个接口,从接口中获取record_id之后,再调用另外一个接口,将获得的record_id传递到第二个接口中,然后从第二个接口返回goods_name,那么我们来看一下用以前的方式和使用ES7的async/await方式来编写的不同。 
一开始,我使用的方式如下


apis.postExchangeGoods({},{goods_id: this.current_goods.id})
  .then(res => {
    if (res.data.errcode === 0) {
      let record_id = res.data.record_id;
      apis.postUserInfo({},{record_id: record_id})
        .then(res => {
          this.show = false;
          let goods_name = res.data.goods_name;
          this.$router.push({ 
            name: 'xxx'
          });
        });
      });
    }
});


可以看到这种方式下,在一个接口的回调中调用另一个接口,代码不仅多,看起来也比较杂乱,而且此时只是调用2个接口,如果在某些需求下我们要调用四五个接口甚至十个接口,那么此时代码不仅看起来特别乱,而且会各种回调接口的嵌套,维护起来也不方便,掉入传说中的callback hell。而es7中推出async/await函数就可以解决这种问题,我们来看一下使用这种方式后写出来的代码:


async function postUserInfo() {
 try {
   var result1 = await window.apis.postExchangeGoods({}, {goods_id: _this.goods_id});//里面必须return  或者是resolve()结果
   var result2 = await window.apis.postUserInfo({},{record_id:resul1.data.rescord_id});
   return result2.data.goods_name;
  } catch(e) {
   console.log(e);
 }
}
postUserInfo().then((goods_name) => {
 this.$router.push({ 
    name: 'xxx'
  });
});


第二种写法需要使用async function去定义一个方法,那么这个方法最后会返回一个Promise对象。 
在这个方法中调用那两个接口,第3行代码中使用了await命令,那么此时该方法会等待第3行的异步操作执行完毕之后,才会继续执行下面的代码,如果由于网络问题该postExchangeGoods请求了5秒,那么代码就会在这里等待5秒,直到获取返回结果,然后再执行下面的代码。


当async方法里面的内容全部执行完毕之后,就会调用第10行中的then指定的回调方法。


需要注意的是:await后面跟着的函数,必须通过Promise.resolve或者Promise.reject来返回某些内容作为第二个异步函数的参数!比如.then((res)=>{}
这里的res就是上一个异步函数返回的参数
同时需要注意的是reject 不需要return 


我们对比一下这两种写法,可以看得出,第二种方法首先代码比较简洁,调用两个接口只写了第3行和第4行代码,然后用一个变量来获取返回的结果。而且更重要的是,他脱离了第一种写法的那种几个回调函数嵌套的现象,代码变得更清晰,维护起来也更好。当然,由于async/await是es7中推出的API,使用babel并不能将其转译为大部分浏览器支持的ES5,此时我们需要使用垫片。

举个比较实战的例子:vue项目中数据请求组件

import {
baseUrl
} from './env'  //定义的请求头配置


export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
type = type.toUpperCase();
url = baseUrl + url;


if (type == 'GET') {
let dataStr = ''; //数据拼接字符串
Object.keys(data).forEach(key => {//如果请求方式为get则拼接url参数
dataStr += key + '=' + data[key] + '&';
})


if (dataStr !== '') {
dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
url = url + '?' + dataStr;
}
}


if (window.fetch && method == 'fetch') {//如果当前浏览器支持fetch方法则
let requestConfig = {//请求参数配置
credentials: 'include',
method: type,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode: "cors",
cache: "force-cache"
}


if (type == 'POST') {//如果请求方式为post  则给该对象新增一个属性body 里面放置请求参数
Object.defineProperty(requestConfig, 'body', {
value: JSON.stringify(data)
})
}

try {
const response = await fetch(url, requestConfig);//fetch里封装了异步请求逻辑  是 浏览器自带的方法
const responseJson = await response.json();
return responseJson //返回数值作为回调函数的参数
} catch (error) {
throw new Error(error)
}
} else {//如果不支持fetch 则利用Promise+原生AJAX的方式请求数据
return new Promise((resolve, reject) => {
let requestObj;
if (window.XMLHttpRequest) {
requestObj = new XMLHttpRequest();
} else {
requestObj = new ActiveXObject;
}


let sendData = '';
if (type == 'POST') {
sendData = JSON.stringify(data);
}


requestObj.open(type, url, true);
requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
requestObj.send(sendData);


requestObj.onreadystatechange = () => {
if (requestObj.readyState == 4) {
if (requestObj.status == 200) {
let obj = requestObj.response
if (typeof obj !== 'object') {
obj = JSON.parse(obj);
}
resolve(obj)//将结果导出
} else {
reject(requestObj)
}
}
}
})
}

}


注明:该文章参考了多人博文以及结合自己的总结!

猜你喜欢

转载自blog.csdn.net/weixin_41421227/article/details/80949944
今日推荐