在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)
}
}
}
})
}
}
es6对象修改的新方式
let obj1 = {
name:1,
sex:"man"
}
let obj2 = {
sex:"woman"
}
const obj3 = {
...obj1,
...obj2,
}
这样sex就变成了 sex:"woman"
注明:该文章参考了多人博文以及结合自己的总结!