Promise 概述
- Promise是ES6中提供的优化异步操作的解决方案。解决了ES6之前异步操作,反复嵌套回调函数所导致的回调地狱问题。
- 从语法上来讲,Promise是一个构造函数,它可以获取异步操作的请求,且返回一个结果。Promise有三种状态:pending(等待),fulfiled(成功),rejected(失败)。状态一旦改变,就不会再变。创造Promise实例后,它会立即执行。
什么是回调地狱
在开发中,会遇到一个异步请求后再执行另一个异步请求的情况。例如请求一个列表数据,之后根据列表找到索引,再去请求分类…一直请求,每一次异步请求,都需要反复嵌套回调函数,导致代码可读性大大降低,后期难以维护。
创建Promise实例
- 创建一个Promise对象。
- 调用
then()
和catch()
去处理结果。
let fullPromise = new Promise((resolve,reject) => {
if (true) {
resolve('Success Promise');
} else {
reject('Error Promise');
}
})
console.log(fullPromise);
fullPromise.then((res) => {
console.log('resolved',res);
},(err) => {
console.log('rejected',err);
}
)
fullPromise.catch((res) => {
console.log(res);
})
创建成功的Promise对象——简写resolve
let p = Promise.resolve('Success Promise');
创建失败的Promise对象——简写reject
let p = Promise.reject('Error Promise');
Resolve(),Reject()
- Promise对象是由关键字
new
及其构造函数来创建的,构建函数后,会把一个叫做“处理器函数”(executor function)的函数作为它的参数,并且传入两个参数:resolve
,reject
,当异步任务顺利完成返回结果时,会调用resolve
,而当异步任务失败返回结果时(通常是一个错误对象),会调用reject
。 - 其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,
resolve
是将Promise的状态置为fullfiled
,reject
是将Promise的状态置为rejected
。
let fullPromise = new Promise((resolve,reject) => {
if (true) {
resolve('Success Promise');
} else {
reject('Error Promise');
}
})
console.log(fullPromise);
这里if
设置为true
,也就是输出resolve('Success Promise')
。
打印结果:
将if
设置为false
,也就是输出reject('Error Promise')
。
let fullPromise = new Promise((resolve,reject) => {
if (false) {
resolve('Success Promise');
} else {
reject('Error Promise');
}
})
console.log(fullPromise);
打印结果:
可以看到,通过调用resolve()
和reject()
改变了Promise对象的状态。
Then()的用法
then()
是定义在原型上的 Promise.prototype 。then()
之后会返回一个新的promise对象。then()
中可以传入两个参数,是函数。第一个对应resolve
的回调,第二个对应reject
的回调。
let fullPromise = new Promise((resolve,reject) => {
if (true) {
resolve('Success Promise');
} else {
reject('Error Promise');
}
})
console.log(fullPromise);
fullPromise.then((res) => {
console.log('resolved',res);
},(err) => {
console.log('rejected',err);
}
)
打印结果:
可以看到then()
对应resolve()
获取到了结果res
。
将if
设置为false
:
let fullPromise = new Promise((resolve,reject) => {
if (false) {
resolve('Success Promise');
} else {
reject('Error Promise');
}
})
console.log(fullPromise);
fullPromise.then((res) => {
console.log('resolved',res);
},(err) => {
console.log('rejected',err);
}
)
打印结果:
可以看到then()
对应reject()
获取到了结果err
。
通过then()
就能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
链式操作的用法
- 从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。
- 可以在
then()
中继续写Promise对象并返回,然后继续调用then()
来进行回调操作。
所以使用Promise的正确场景是这样的:
function runTimeP(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP Success');
},2000);
});
return timeP;
}
runTimeP()
.then((res) => {
console.log('resolved1',res);
return runTimeP();
},(err) => {
console.log('rejected1',err);
}
)
.then((res) => {
console.log('resolved2',res);
return runTimeP();
},(err) => {
console.log('rejected2',err);
}
)
.then((res) => {
console.log('resolved3',res);
return runTimeP();
},(err) => {
console.log('rejected3',err);
}
)
打印结果:
- 在上面的代码中,执行了一个异步操作
setTimeout()
,2秒后,调用resolve()
。 - 在
runTimeP()
中传给resolve
的数据,通过直接return
Promise对象,能在接下来的then()
中拿到。 - 实现了按顺序每隔两秒输出异步请求返回的结果,而且解决了回调地狱的问题。
Catch()的用法
catch()
和then()
的第二个参数一样,用来指定reject
的回调,效果和写在then()
第二个参数里面一样。catch()
还有另外一个作用:在执行resolve
的回调时,还会捕捉resolved
抛出的异常。
function runTimeP(){
let timeP = new Promise((resolve,reject) => {
if (true) {
setTimeout(function(){
resolve('TimeP Success');
},2000);
} else {
reject('Time Error');
}
});
return timeP;
}
runTimeP()
.then((res) => {
console.log(message); //未定义message
console.log(res);
})
.catch((res) => {
console.log('rejected');
console.log(res);
})
打印结果:
- 上方代码中,在
then()
中打印了一个未声明的变量message
,本来应该报错,但是Js没有报错,而且还运行了catch()
中的内容。
All()的使用
- Promise的
all()
提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。 all()
的参数是对象数组。数组内所有的Promise对象状态为fulfiled
时,该对象就为fulfiled
。只要数组中有任意一个Promise对象状态为rejected
,该对象就为rejected
。
let num = 0;
let timer = setInterval(function(){
num++;
console.log(num);
},1000)
function runTimeP1(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP1 Success');
},3000);
})
return timeP;
}
function runTimeP2(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP2 Success');
},5000);
})
return timeP;
}
function runTimeP3(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP3 Success');
},10000);
})
return timeP;
}
Promise.all([runTimeP1(),runTimeP2(),runTimeP3()])
.then((res) => {
clearInterval(timer);
console.log(res);
})
打印结果:
- 三个Promise对象都为
fulfiled
,设置了setTimeout()
,分别是3秒后,5秒后,10秒后。 - 可以看到,使用了
all()
之后,所有异步操作执行完后才执行回调。
其中Promise对象存在rejected
的情况:
let num = 0;
let timer = setInterval(function(){
num++;
console.log(num);
},1000)
function runTimeP1(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP1 Success');
},3000);
})
return timeP;
}
function runTimeP2(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
reject('TimeP2 Error');
},5000);
})
return timeP;
}
function runTimeP3(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP3 Success');
},10000);
})
return timeP;
}
Promise.all([runTimeP1(),runTimeP2(),runTimeP3()])
.then((res) => {
clearInterval(timer);
console.log(res);
})
.catch((res) => {
clearInterval(timer);
console.log(res);
})
打印结果:
- 因为其中有一个Promise对象的状态为
rejected
,导致all()
的状态也为rejected
,结果并没有返回。 - 有了
all()
,就可以并行执行多个异步操作,并且在一个then()
中处理所有的返回数据。
Race()的用法
all()
是在所有异步操作执行完后才执行回调。与all()
相反,第一个异步操作执行完后,以它为准执行回调,这就是race()
。race()
的用法和all()
一样,race()
的参数也是数组对象,当数组中所有的Promise对象,有一个先执行了何种状态,该对象就为何种状态,并执行相应函数。
function runTimeP1(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP1 Success');
},3000);
})
return timeP;
}
function runTimeP2(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
reject('TimeP2 Error');
},5000);
})
return timeP;
}
function runTimeP3(){
let timeP = new Promise((resolve,reject) => {
setTimeout(function(){
resolve('TimeP3 Success');
},10000);
})
return timeP;
}
Promise.race([runTimeP1(),runTimeP2(),runTimeP3()])
.then((res) => {
clearInterval(timer);
console.log(res);
})
.catch((res) => {
console.log(res);
})
打印结果:
race()
执行了最早运行的runTimeP1()
。race()
可以用在请求获取上,设置setTimeout()
,当获取超时,race()
就会执行setTimeout()
。