之前用vue写了一个表格组件,一个页面最多会循环出来20个表格,每个表格都有会发请求,差不多20个请求一起发出,数据库不干了,所以就想在一个表格请求完再请求下一个表格。
这就用了传说中的promise,前面稍微解释一下promise,后面直接贴代码
Promise其实是一个构造函数,自带有all、reject、resolve这几个方法,用的时候有then、catch这俩方法
一、先来new一个
var p = new Promise(function(resolve, reject){
//做一些异步操作(可以是一个post请求)
setTimeout(function(){
console.log('执行!');
resolve('数据');
}, 2000);
});
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected,现在不用太纠结这个
运行,两秒之后输出“执行!”,并没用调用,new以后就自动执行了,所以promise需要包在函数里,需要的时候再调用
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行!');
resolve('数据');
}, 2000);
});
return p;
}
runAsync()
包装好之后,还是不知道怎么实现最初的需求,而且resolve(‘数据’);不知道什么意思
包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。还记得Promise对象上有then、catch方法吧?这就是强大之处了
二、来调用一下
runAsync().then(function(data){
console.log(data);
//这里的data,就是刚刚resolve('数据');括号里传过来的东西
//后面可以用传过来的数据做些其他操作
//......
});
运行,会输出“执行!”,接着输出“数据”
所以能看出来,then其实就是我们在promise的异步操作的回调,现在就是把异步操作和回调分开写了,在一步操作之后,链式执行回调。这个时候会有疑问,不用promise,直接在setTimeout里调用runAsync().then里的函数不就好了,所以就用到了promise的最厉害的链式操作
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
runAsync1、runAsync2、runAsync3这三个函数都是如何定义的?
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;
}
就是上面这样,第一个异步操作之后执行第二个,第二个之后执行第三个,当然如果异步操作一样的话,可以自己调用自己,也就是写个递归,后面贴代码
在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了,比如:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return '直接返回数据'; //这里直接返回数据
})
.then(function(data){
console.log(data);
});
运行,结果是:
异步任务1执行!
数据1
异步任务2执行!
数据2
直接返回数据
关于reject的用法
function isAdult(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var age = Math.ceil(Math.random()*30); //生成1-30的随机数
if(age>=18){
resolve(age);
}
else{
reject('还没成年');
}
}, 2000);
});
return p;
}
isAdult().then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason, data){
console.log('rejected');
console.log(reason);
}
);
isAdult
函数用来异步得到一个数字,2秒后执行完成,如果数字小于等于18,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。
关于catch的用法
Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调,用法是这样:
isAdult()
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。
三、代码
//封装一个Promise函数
tablePromise(tableIndex){
var p = new Promise(function(resolve, reject){
//做一些异步操作
this.$post(this.basePath + 'table_data').then(data => {
resolve(data.data)
});
});
return p;
},
//调用封装的方法
getTable(){
this.tableIndex++
tablePromise(this.tableIndex).then(function (data) {
this.dataList.push(data)
if(this.tableIndex< this.list.length){
this.getTable() //这里做递归
}
})
}