文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
1.回调函数
在学习promise之前,大家先要知道什么是回调函数?
回调函数其实就是把一个函数当做参数传递给另一个函数:
function start(callBlak){
let i = 1;
let time = null;
time = setInterval(function(){
i--;
if( i < 0 ){
clearInterval(time)
callBlak? callBlak() : '';
}
},1000)
}
start(function(){ console.log('hello world'); })
2.回调地狱
在某些逻辑下,使用回调函数可能造成恐怖的回调地狱!
如经典的Ajax的success就是回调函数,只有得到一个接口服务器返回的某个信息,我们才能拿这这个信息去做新的请求,循环往复,代码会变得十分的恐怖!
例子:(可以略过,大概理解上面的话就可以了,后面会讲解决方案)
$.ajax({
url:'http://www.xxx.com/index.php/index/goods/userinfo',
method:'post',
data:{
username:'a123123'
},
success:res=>{
$.ajax({
url:'http://www.xxx.com/index.php/index/goods/orderinfo',
method:'post',
data:{
id:res.info.user_id
},
success:userres=>{
$.ajax({
url:'http://www.xxx.com/index.php/index/goods/goodsinfo',
method:'post',
data:{
id:userres.info.goods_id
},
success:goosres=>{
console.log(goosres);
}
})
}
})
}
})
3.同步异步
同步:等待上一同步任务执行完毕,才执行下一个同步任务
异步:等待所有同步任务执行完毕,再执行异步任务
常见的异步操作:setIntervalval、setTimeout、ajax
所有的同步任务都在主线程中执行,在主线程之外有一个任务队列,所有的异步任务都会进入任务队列,等待同步任务执行完毕之后执行
4.Promise
Promise对象就是专门用来解决异步问题的。
Promise有三个状态:pending(准备状态)、fulfilled(成功状态)、rejected(失败状态)
Promise还有两个方法:resolve() 、 reject(),通过这两个方法可以改变状态。
pending(准备状态) -> resolve() -> fulfilled(成功状态)
pending(准备状态) -> reject() -> rejected(失败状态)
并且,当改变发生之后,状态就会定型,后续通过方法可以获取对应状态的结果。
举个例子:
function pro(){
return new Promise((reolve,reject)=>{
console.log('异步程序'); // 模拟ajax
if(true){
reolve('成功')
}else{
reject('失败')
}
})
}
pro().then(function(res){
// res为 reolve 返回的值
console.log(res);
})
new Promise生成promise实例之后,可以通过 then 方法来指定resolve和reject的回调函数
then方法中可以继续 return 一个promise对象,然后返回值就是这个promise对象
这里有个细节大家需要注意一下,就是new Promise构建promise对象会立即执行,所以一般会把new Promise封装在一个函数中,保证只有调用时才执行。
最后则是关于promise错误状态的处理(可能出现的reject状态)
解决方法就是catch,在最后用catch捕获错误,返回错误信息
function p1(){
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve({a:1,b:2})
},1000)
})
return p;
}
function p2(){
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('hello world')
},1000)
})
return p;
}
p1().then((res) => {
return p2();
}).then((res) => {
console.log(res)
}).catch((err) => {
console.log("err",err)
})
catch只需要在最后写一次,无论中途哪里出现reject状态,都会进入catch。
下面简单说一下,Promise对象的几个方法
Promise.finally()
finally()方法用于指定不管Promise对象最后的状态如何,都会执行的操作。
finally()方法不接收任何参数,并且与Promise对象的状态无关,所以finally方法中执行的逻辑应该是跟promise对象逻辑无关的一个逻辑操作。
Promise.all()
Promise.all()方法接收多个promise实例作为参数,这样就可以把异步请求封装到promise实例中,然后同时发起请求。
在实际应用中,同时并发的操作并不常见,而且还很容易出现问题。
因为只要出现一次reject,最后会进入catch方法,并且只得到reject的结果。
这里直接上解决方法:
给每个参数promise实例添加一个catch方法,当有错误发生时,错误会进入自身的catch方法,并且自身catch方法return的数据,将作为最终结果进入到all方法的then中。
let p1 = new Promise((reolve,reject)=>{
if(true){
reolve('成功1')
}else{
reject('失败1')
}
}).catch(err=>err)
let p2 = new Promise((reolve,reject)=>{
if(false){
reolve('成功2')
}else{
reject('失败2')
}
}).catch(err=>err)
let p3 = new Promise((reolve,reject)=>{
if(false){
reolve('成功3')
}else{
reject('失败3')
}
}).catch(err=>err)
Promise.all([p1,p2,p3]).then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err);
})
Promise.race()
跟 Promise.all()方法一样,同时执行多个实例,哪个先确定状态,race方法就以哪个状态为主
let p1 = new Promise((reolve,reject)=>{
setTimeout(()=>{
if(true){
reolve('成功1')
}else{
reject('失败1')
}
},3000)
}).catch(err=>err)
let p2 = new Promise((reolve,reject)=>{
setTimeout(()=>{
if(false){
reolve('成功2')
}else{
reject('失败2')
}
},2000)
}).catch(err=>err)
let p3 = new Promise((reolve,reject)=>{
setTimeout(()=>{
if(false){
reolve('成功3')
}else{
reject('失败3')
}
},1000)
}).catch(err=>err)
Promise.race([p1,p2,p3]).then((res)=>{
console.log(res);
}).catch(err=>console.log(err))
Promise.allSettled()
Promise.allSettled()方法专门用来处理Promise.all()出错的情况。不管参数Promise实例的状态如何,最终都进入到成功结果(then)中去。只是结果中通过特定的值方式表明成功或者失败情况。
let p1 = new Promise((reolve,reject)=>{
return Math.random() > 0.5 ? reolve('成功1') : reject('失败1');
})
let p2 = new Promise((reolve,reject)=>{
return Math.random() > 0.5 ? reolve('成功2') : reject('失败2');
})
let p3 = new Promise((reolve,reject)=>{
return Math.random() > 0.5 ? reolve('成功3') : reject('失败3');
})
Promise.allSettled([p1,p2,p3]).then((res)=>{
console.log(res);
}).catch(err=>console.log(err))
5.async
async可以将普通函数转换成Promise实例!
async function abc(){
}
let res = abc();
console.log( res ); // Promise对象
async函数的返回值将作为Promise实例的resolve的结果,这里就是操作Promise对象的操作了!
async function abc(){
return 'hello world'
}
abc().then(function(res){
console.log(res);
})
6.await
await就是等待的意思,只可以用在async函数中(用在普通函数中会报错),他一般用来修饰一个Promise对象,等待promise的状态确定之后,等待状态才会结束! 并且await命令会返回他修饰的promise对象的resolve值
注意:只会返回resolve值,reject值会终止async函数
async function abc(){
let a = await new Promise(function(reolve,reject){
setTimeout(function(){
console.log('异步任务');
reolve('成功')
},3000)
})
console.log('同步任务');
}
abc()
大家可以复制上面的例子看一下控制台。
通过await已经实现了让同步等待异步的操作,变相的把一个异步任务变成了同步任务
现在我们回头看一下一开始的回调地狱例子,用async、await来解决会变得十分简单
let url1 = 'http://www.xxx.com/index.php/index/goods/userinfo';
let url2 = 'http://www.xxx.com/index.php/index/goods/orderinfo';
let url3 = 'http://www.xxx.com/index.php/index/goods/goodsinfo';
function getUser(userUrl,userData){
return new Promise((reolve,reject)=>{
$.ajax({
url:userUrl,
method:'post',
data:userData,
success:(res)=>{
reolve(res)
},
error:(err)=>{
reject(err)
}
})
})
}
async function start(){
let Userid = await getUser(url1,{username:'a123123'})
let orderid = await getUser(url2,{id:Userid.info.user_id})
let goodsinfo = await getUser(url3,{id:orderid.info.goods_id})
console.log(goodsinfo);
}
start().catch(err=>console.log(err))
由于我用的是本地模拟服务器,所以你们看不了结果了,我们主要还是看逻辑。
这里的执行逻辑会变成,大家对程序最开始的理解,因为ajax已经被变成了同步任务,所以逻辑就变成了,一行代码结束,下一行代码才会开始执行。
最后说一下:关于async函数的错误状态处理
在async函数中,如果函数体中出现程序错误(变量未定义、方法名字写错…)或者await 后边的promise对象 返回reject状态,都会终止async函数的运行,进入async函数的catch方法中(相当于promise状态变为reject失败状态)。
解决方法:给每个Promise对象添加 catch() 方法,或者直接用 try/catch方法,篇幅有限,这里先暂时不表。