一、Promise概述
Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。
回调函数实现异步编程
let fn = function(callback){
console.log(111);
setTimeout(function(){
callback()
},1000)
}
function b(){
console.log(222)
}
fn(b)
缺点就是当我嵌套多层的时候后期代码很难维护。
二、Promise
- Promise格式
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value); //可以传递参数,如value
} else {
reject(error);
}
});
Promise构造函数接受一个函数(回调函数)作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。then方法是为promise对象添加状态时的回调函数
promise.then(function(value) { //接收res()函数传递过来的值
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
promise的then方法的第二个参数和catch区别:
- 如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。
- then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。
所以Promise的完整用法就是:
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
}).catch(error){
console.log(error)
}
then和catch也创建并返回一个新的 Promise,它可以 用于表示 Promise 链流控制
- 多个Promise的格式:
let fn = function(){
return new Promise((res,rej)=>{
// ... some code
res("易驰es6") //可以传递参数
})
}
fn().then((res)=>{
return new Promise(function(res,rej){ //第二次Promise
// ... some code
res()
})
}).then(()=>{
return new Promise(function(res,rej){
res()
})
}).then(()=>{
console.log("第三次Promise") //可以无限次的添加Promise
})
注意:Promise新建后就会立即执行,👇举例来说明:
举个🌰
{
let fn = function(){
console.log(111);
return new Promise((res,rej)=>{
console.log(555);
res()
})
}
fn().then(()=>{
console.log(444)
})
console.log(333)
//111
//555
//333
//444
}
fn()被调用以后先输出“111”,接着Promise新建后立即执行,所以首先输出的是“555”。然后,输出“333”,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以“444”最后输出
但如果加入定时器会有不一样的结果,👇
再来个🌰
{
let fn = function(){
console.log(111);
return new Promise((res,rej)=>{
setTimeout(() => {
console.log(222)
}, 1000);
res()
})
}
fn().then(()=>{
console.log(444)
})
//111
//444
//222
}
当有两个Promise和定时器的时候:👇
举个🌰:
{
let fn = function(){
console.log(111);
return new Promise((res,rej)=>{
setTimeout(() => {
console.log(222)
}, 1000);
res()
})
}
fn().then(()=>{
console.log(444);
return new Promise(function(res,rej){ //嵌套第二层Promise
setTimeout(()=>{
console.log('aaa')
},1000)
res()
})
}).then(()=>{
console.log('bbb')
})
//111 444 bbb 222 aaa
}
上面代码中,fn()先执行输入111,然后遇到Promise,本应该立即输出,可是遇到了定时器,所以会先把定时器放入执行队列中等待执行,此时会执行then里边的回调函数,输出444,输出以后再次遇到Promise,本应该立即输出但是同样遇到了定时器,依然是先把定时器放入到执行队列中等待执行,所以不会输出aaa,而是去执行了then回调函数输入了bbb,直到此时剩下两个定时器等待执行,会根据定时器的毫秒数来执行定时器。会先执行快要到时的那个定时器,因此上述例子两个定时器都是1秒以后执行,所以会先执行先放入队列的那个定时器。
假如定时器的毫秒数不一样时:👇
再举个🌰
{
let fn = function(){
console.log(111);
return new Promise((res,rej)=>{
setTimeout(() => {
console.log(222)
}, 5000);
res()
})
}
fn().then(()=>{
console.log(444);
return new Promise(function(res,rej){ //嵌套第二层Promise
setTimeout(()=>{
console.log('aaa')
},1000)
res()
})
}).then(()=>{
console.log('bbb')
})
//111 444 bbb aaa 222
}
前边的三个输出结果依然不变,当5秒定时器和1秒定时器,会先执行1秒定时器再执行五秒定时器。
总结:当函数中包括Promise,调用函数先执行Promise之前的代码,当遇到Promise立即执行Promise里边的代码,将在当前脚本所有同步任务执行完才会执行,then方法指定的回调函数,如果含有定时器定时器代码将按顺序执行。
定时器总结:定时器属于异步操作,遇到定时器会先把定时器放入等待执行的队列中,等到队列中的哪个定时器到了时间以后就会自动执行。其他同步代码的执行完以后按顺序执行定时器。
最后来个🌰
{
let fn = function(){
console.log(111);
return new Promise((res,rej)=>{
setTimeout(() => {
console.log(222)
}, 5000);
res()
})
}
fn().then(()=>{
console.log(444);
return new Promise(function(res,rej){ //嵌套第二层Promise
setTimeout(()=>{
console.log('aaa')
},1000)
res()
})
}).then(()=>{
console.log('bbb')
})
console.log("sss");
//111 sss 444 bbb aaa 222
}
三、Promise方法
- Promise.all([p1,p2...])
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例,同时执行,并发的状态,要可以都可以,要不可以都不可以。
{
let loadImg = function(srcUrl){
return new Promise((res,rej)=>{
let imgs = document.createElement("img")
imgs.src = srcUrl;
imgs.onload = function(){
res(imgs)
}
})
}
Promise.all([
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg')
]).then(function(img){
img.forEach(function(item){
document.body.appendChild(item)
})
})
}
就是四个调用同时执行,要成功都成功。并发执行。
- Promise.race()
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。它和all的区别就是优先加载一个,哪个速度比较快显示哪个
{
let loadImg = function(srcUrl){
return new Promise((res,rej)=>{
let imgs = document.createElement("img")
imgs.src = srcUrl;
imgs.onload = function(){
res(imgs)
}
})
}
Promise.race([
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg')
]).then(function(img){
document.body.appendChild(img)
})
}





网友评论