Promise是JS中进行异步编程的新解决方案(ES6规范)
异步任务:例如文件IO,AJAX,定时器等等,异步任务,简单地说,异步任务就是把任务先放一放,等当前手头的任务处理完之后再处理。
在Promise出现之前,我们想要执行异步函数,必须先指定其回调。
如果想要在基于第一个异步任务的处理结果执行第二个异步任务,那么我们就需要再第一个异步任务的回调函数中编写第二个异步任务的回调函数。
这样会产生一个回调地狱的问题。
即
如图所示,代码疯狂缩进,回调函数疯狂嵌套,即不便于阅读,也不利于异常处理
于是就产生了Promise这种解决方案,Promise通过链式调用的解决方案,解决回调地狱问题,同时能有效的处理异常。
链式调用,简单地说就是promise.then().then().then()通过.连接像一条锁链一样一个一个接着调用。
我们可以通过Promise执行流程图对Promise有一个简单的认识。
再看一下Promise的结构,关键的有
Promise的使用分为三个流程:改变Promise状态,指定回调,执行回调
如何改变Promise状态?调用resolve或reject,每个Promise的状态只能修改一次
如何指定回调?首先获取promise实例对象,调用实例对象身上的.then()函数,并传入两个函数,第一个是成功的回调(resolve调用时执行),第二个是失败的的回调(reject调用时执行)。 then可以只指定成功的回调,如果只指定失败的回调的话使用.catch(()=>{})其中传入失败的回调
如何执行回调?promise状态改变,即resolve或reject调用的时候自动执行
解决了上述三个问题,我们来看看具体的代码
使用Promise首先我们需要创建一个Promise对象(new Promise()),此时我们Promise的状态是pendding,然后在其中传入一个执行函数,由于我们当前函数内执行内容没有异步任务,该函数的内容是同步执行的。
let p = new Promise((resolve,reject)=>{//在这个位置Promise的状态是pendding
//执行一些任务
if(success)
resolve() //如果调用,则Promise状态修改为resolved
else
reject() //如果调用,则Promise状态修改为rejected
})
由于一个Promise的状态只能修改一次,因此如果在Promise执行器函数中两个resolve和reject都调用,Promise的状态取决于先执行的修改状态函数
let p = new Promise((resolve,reject)=>{
resolve() //Promise状态修改为resolved
reject() //状态不修改
})
指定回调用Promise实例对象身上的.then()
p.then(
(res)=>{//成功的回调当resolve执行时执行
console.log("success")
),
(res)=>{//失败的回调,当reject执行时执行
console.warn("fail")
})
调用顺序如下
let p = new Promise((resolve,reject)=>{ 1//状态为pendding
//执行一些任务
resolve() 2//执行resolve(),状态为resolved
reject()
})
p.then( 3//执行回调函数
(res)=>{
console.log("success") 4//执行成功的回调
),
(res)=>{
console.warn("fail")
})
Promise只执行同步任务显然没发挥出其真正威力,毕竟Promise就是专门解决异步编程问题的,当Promise 内执行函数为异步,执行顺序如下
let p = new Promise((resolve,reject)=>{ 1//状态为pendding
setTimeOut( //这里设置了定时器,将任务转化为异步
()=>{
//执行一些任务
resolve() 3//Promise改变状态为resolved
reject()
},1000
)
})
p.then( 2//指定回调函数,此时回调函数已被指定
//即成功时执行第一个函数,失败时执行第二个函数,但是函数未执行,
(res)=>{
console.log("success") 4//执行成功的回调
),
(res)=>{
console.warn("fail")
})
关键总结:只有调用resolve或reject,才执行回调
.then()的返回值是Promise实现链式调用的关键,当我们的.then()内函数的返回值是除了Promise的任意值时,返回一个新的resolved状态的Promise对象,新Promise对象的结果就是.then()内函数的返回值
let res = p.then(
(res)=>{
console.log("success")
//如果没有return,则返回值未undefined
return "123"
),
(res)=>{
console.warn("fail")
})
//此时res是一个resolved状态的Promise,其PromiseResult是123
如果抛出异常,则res的状态为失败,结果为异常内容
let res = p.then(
(res)=>{
console.log("success")
throw "fail"
),
(res)=>{
console.warn("fail")
})
//此时res时一个reject状态,fail内容的Promise对象
如果返回的是一个Promise对象,则.then()的返回值就是该Promise对象
let res = p.then(
(res)=>{
console.log("success")
return new Promise((resolve,reject)=>{
resolve("success")
)
),
(res)=>{
console.warn("fail")
})
//此时res时一个resovled状态,success内容的Promise对象
由于返回值均为Promise对象,因此我们就可以进行链式调用了。
Promise.resolve(value) 如果value是一个非Promise对象,则函数返回值是一个Promise对象,结果为value
如果value是一个Promise对象,则返回值就是该Promise对象
Promise.reject(reason) 返回一个失败的Promise对象,结果为reason
Promise.all(promises) 返回一个新的promise,promises是一个包含多个promise的数组,当所有promise状态都为resolved,返回的promise状态才为resolved,状态为rejected
Promise.race(promises) 返回一个新的promise,promises是一个包含多个promise的数组,只要有其中一个promise先改变状态,其状态就是返回的promise的状态,其结果就是返回的promise的结果。
当使用promise.then()的链式调用时,可以在最后指定失败的回调,前面出了任何异常,都会传到最后的失败回调中处理
(前提是之前的所有函数都未指定失败的回调)
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
// console.log(111);
throw '失败啦!';
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
//在第一个.then()中的异常,在最后处理,之前所有成功的回调都不会执行
});
async就是Promise对象的语法糖,async function a 相当于Promise.resolve(function a)
await如果右侧为promise则返回promise
通常我们都将async和await同时使用
await中如果抛出异常,我们可以在async中捕获异常并处理,如果抛出异常,则async在await抛出异常之后的代码将无法执行。
await只阻塞当前作用域,即函数执行到await那一行时,当前作用域下还未执行所有代码都等到await异步任务执行完毕之后再执行,而不会影响其他作用域函数的执行
async function main(){
let p = new Promise((resolve, reject) => {
// resolve('OK');
reject('Error');
})
//1. 右侧为promise的情况
// let res = await p;
//2. 右侧为其他类型的数据
// let res2 = await 20;
//3. 如果promise是失败的状态
try{
let res3 = await p;
}catch(e){
console.log(e);
}
}