一.什么是Promise
官方定义: Promise对象用于表示一个异步操作的最终完成(或者失败)及其结果值。它可以把异步操作最终的成功返回值或者失败原因和相对应的程序关联起来。这样就使得异步方法可以像同步方法那样返回值;但是异步方法不会立即返回最终的值,而是会返回一个Promise,在未来某个时刻把值交给使用者。通俗来说,Promise就是一个容器,里面存放着一个异步操作的状态。
二.为什么使用Promise
使用Promise主要是为了解决JS中的回调地狱问题,详见这篇博客
写文章-CSDN创作中心浅谈JS中的回调地狱-CSDN博客写文章-CSDN创作中心
三.Promise的使用
3.1接收的参数
首先实例化Promise对象,在实例化Promise的时候,传入的参数是一个函数,函数中接收两个参数,resolve和reject.传入的resolve和reject本身都是函数,这两个函数分别执行如下效果:
1.resolve:把Promise的状态从进行中变为成功状态
2.reject:把Promise的状态从进行中变为拒绝状态(失败)
3.2三种状态
Promise是一个状态机,分为三种状态
1.pending:进行中,表示Promise还在执行阶段,还没有执行完成,也可以理解为初始化状态
2.fulfilled:成功,表示Promise执行成功
3.rejected:拒绝状态,表示Promise执行被拒绝,也就是失败
//传入回调函数时的状态,还未开始执行
const a=new Promise((resolve,reject)=>{
})
console.log(a);
//调用resolve回调函数时的状态
const b=new Promise((resolve,reject)=>{
resolve('123')
}).then(res=>{
console.log(res);
})
console.log(b);
// 调用reject回调函数时的状态
const c=new Promise((resolve,reject)=>{
reject('失败')
}).catch(rej=>{
console.log(rej);
})
console.log(c);
从上面的演示可以看出,根据传入的回调函数不同,Promise的状态会发生相应的改变,并且Promise的状态,只可能是其中一种状态,状态固定后,就不会再发生改变。
Promise.then
在执行reslove的时候,Promise的状态会变为fulfilled,会执行.then方法,then方法中接收的参数也是一个函数,函数中携带一个参数,参数是resolve返回的数据。
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功执行')
})
}).then(res=>{
console.log(res);
})
console.log(p);
Promise.catch
在执行reject时,Promise状态从pending变为reject,就会执行catch方法,catch方法中接收的参数也是一个函数,函数中携带一个参数,为reject返回的数据,但是如果使用catch,Promise的最终状态会是fulfilled,这是因为catch方法对Promise的拒绝状态做了处理,所以Promise的状态会变为fulfilled。
不使用catch方法,相当于没有对Promise的拒绝状态做处理,返回结果如下:
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('失败')
},1000)
}).then(res=>{
console.log(res);
})
console.log(p);
这时Promise的状态为失败,控制台也会报错。
当使用catch来对Promise的拒绝状态做处理时:
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('失败')
},1000)
}).catch(err=>{
console.log(err,p);
})
可以看到,这时Promise的状态就变为了fulfilled。
Promise.finally
finally方法不管Promise对象处于什么状态,都会执行finally方法。finally方法的回调函数不接受任何参数,也就是说,在使用finally方法的时候我们无法判断Promise对象的状态,这也就说明了finally方法的执行与Promise的状态无关,不依赖Promise的执行结果。
const p=new Promise((resolve,reject)=>{
setTimeout(()=>reject('失败'),100);
});
p.then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
}).finally(()=>{
console.log('最终都会执行',p);
})
const p=new Promise((resolve,reject)=>{
setTimeout(()=>resolve('成功'),100);
});
p.then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
}).finally(()=>{
console.log('最终都会执行',p);
})
3.3Promise的链式调用
在我们实际应用场景中,我们一般使用Promise来完成接口的多层嵌套使用,Promise可以把多层嵌套按照线性的方式来进行书写,相比于之前的回调写法,就比较优雅。
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('x')
},1000)
}).then((res)=>{
console.log("res1",res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res+'x')
},1000)
})
}).then((res)=>{
console.log("res2",res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res+'x')
},1000)
})
}).then((res)=>{
console.log("res3",res);
})
输出结果如下:
根据上述的演示我们可以看到,我们使用Promise达到了使方法线性执行的效果,比之前的回调写法会优雅许多。
四.Promise方法
4.1all方法
使用all方法可以实现并行执行异步操作,并且在所有的异步操作完成之后,统一返回所有的结果。
Promise.all([
new Promise(resolve=>resolve('程序一执行成功')),
new Promise(resolve=>resolve('程序二执行成功')),
new Promise(reject=>reject('程序三执行失败'))
]).then(res=>{
console.log(res);
})
执行效果如下:
从上述演示可以看出,all方法返回的是一个数组,数组的长度取决于Promise的个数。在使用all方法的场景一般是游戏场景应用的比较多,比如说游戏在预加载时,请求的各种资源,所有的资源加载完成才可以初始化页面。
4.2race方法:
从字面意思来看,race就是赛跑的意思,谁跑的快,谁就先到达。
Promise.race([
new Promise(resolve => setTimeout(() => {
resolve('第一个程序')
}, 500)
),
new Promise(resolve => setTimeout(() => {
resolve('第二个程序')
}, 200))
]).then(res => {
console.log(res);
})
执行结果如下:
由此我们可以得出使用race方法的结论,race方法接收到的也是一个数组,与Promise对象的个数有关,但是它只返回执行最快的Promise的结果。
4.3any方法
any不像all方法一样返回一组所有的完成值,我们只能通过any方法得到一个成功值;同时any方法也不像race方法一样返回第一个结果值,any方法会忽略到所有被拒绝的promise,直到返回第一个promise成功的值。
const promise1 = new Promise((resolve, reject) => {
setTimeout(reject, 100, 'promise 1 rejected');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 400, 'promise 2 resolved at 400 ms');
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'promise 3 resolved at 200 ms');
});
Promise.any([promise1,promise2,promise3]).then((res)=>{
console.log(res);
})
五.Promise和async,await的区别:
async/await是ES7提出的基于Promise的解决异步的最终方案,其实就是基于Promise的一个语法糖,都是非阻塞式的,他使用同步的写法来写异步代码,一个函数如果加上async,那么该函数就会返回一个Promise。
async function fn1(){
return 1
}
const result=fn1()
console.log(result);
由于使用ansyc返回的 是一个Promise对象,所以我们可以使用.then方法来获取结果值。
async function fn1(){
return 1
}
fn1().then((res)=>{
console.log(res);//结果为1
})
await用于等待一个Promise完成或者是拒绝,它通常与async一起使用,当使用await时,程序会暂停执行该async函数,直到await等待的Promise完成并且传回结果的时候,才会继续执行,所以,这两者的结合使得异步代码写起来更同步化。而且在这个组合中,使用try...catch来捕获错误,使得错误处理更加方便和直观。我们使用这个组合可以将上面Promise的链式写法给优化一下。
//先创建一个Promise实例
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('x')
},1000)
})
async function asyncFunction(){
try{
//等待第一个Promise解析
const res1=await p;
console.log("res1",res1);
//等待第二个Promise解析
const res2=await new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res1+'x');
},1000);
});
console.log("res2",res2);
//等待第三个Promise解析
const res3=await new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res2+'x');
},1000);
});
console.log("res3",res3);
}
//在这里捕获可能出现的任何错误
catch(err){
console.log(err);
}
}
asyncFunction();
总结:
1.语法:在语法上async/await提供了更简洁 ,更直观的一个语法,使异步代码更易读和维护。
2.错误处理:在async/await中,可以直接使用try...catch来捕获异常,而在Promise中,则需要使用catch。
3.代码流程:async/await可以使得异步代码看起来更像一个同步代码,易于阅读和理解。