前言
该文章记录的是一些关于Promise的基础知识,后续将会慢慢添加完Promise相关的所有知识,所有内容均从网上整理而来,加上自己的一些理解,方便在工作中使用。
一. Promise
1. Promise理解
- 抽象表达
- Promise是ES6规范中解决异步编程的新方案
- 旧方案是单纯使用回调函数
- 具体表达
- 从语法上来说,Promise是一个构造函数
- 从功能上来说,Promise对象用来封装一个异步操作并可以获取其成功/失败的结果
2. Promise优点
- 指定回调函数的方式更加灵活
- Promise:启动异步任务-->返回promise对象-->给promise对象绑定回调函数(可以指定多个)
- 旧方案必须在启动异步任务之前指定回调函数
- 支持链式调用,解决回调地狱问题
3. Promise的状态
- 实例对象中的一个属性 [PromiseState]
- pending 未决定的
- resolved/fulfilled 成功状态
- rejected 失败状态
- 状态改变:状态改变不可逆,且只有两种改变方式
- pending --> resolved
- pending --> rejected
4. Promise对象的值
- 实例对象中的属性 [PromiseResult],保存着对象成功/失败的结果
- resolve/reject方法可以修改其值
5. Promise的工作流程
二、Promise的API
1. Promise构造函数:Promise(excutor){}
- excutor函数:执行器--> (resolve,reject)=>{}
- resolve函数: 内部定义成功时调用,可以传参resolve(xxx)
- reject函数: 内部定义失败时调用,可以传参reject(xxx)
- 注意:excutor函数是同步调用
let p = new Promise((resolve, reject) => { //excutor函数是同步调用的 console.log(111) }) console.log(222) //先输出 111,再输出222
2. Promise实例方法
2-1. Promise.prototype.then(onResolved,onRejected)
- onResolved函数:成功时调用--> (res)=>{}
- onRejected函数:失败时调用--> (err)=>{}
then方法返回的是一个新的promise对象
2-2. Promise.prototype.catch(onRejected)
- onRejected函数:失败时调用--> (err)=>{}
//我的个人习惯是then方法只用调用onResolved函数,catch调用onRejected函数,将成功/失败分开
let p = new Promise((resolve, reject) => {
if(xxx){
reject('失败了')
}else{
resolve('成功了')
}
})
p.then(res => {
console.log('成功', res)
}).catch(err => {
console.log('失败', err)
})
3. Promise静态方法
3-1. Promise.resolve()
- 作用:可以将一个值封装,返回一个成功/失败的promise对象
- 如果传递的参数为 非Promise对象,则返回的结果为成功状态的promise对象
- 如果传递的参数为 Promise对象,则参数的结果状态决定了resolve返回的结果状态
let p = Promise.resolve(100) console.log(p) //输出:Promise {<fulfilled>: 100} let p2 = Promise.resolve(new Promise((resolve, reject) => { reject('err') })) console.log(p2) //输出: Promise {<rejected>: 'err'}
3-2. Promise.reject()
- 作用:可以将一个值封装,返回一个失败的promise对象
- 不管传入的是不是Promise对象,则返回的结果都是失败状态的promise对象
let p = Promise.reject(100) console.log(p) //输出:Promise {<rejected>: 100} let p2 = Promise.reject(new Promise((resolve, reject) => { resolve('err') })) console.log(p2) //输出: Promise {<rejected>: Promise}
3-3. Promise.all([xxx])
- 作用:数组包裹n个promise对象,返回一个新的promise对象
- 只有所有的promise状态为成功,新返回的promise对象状态才为成功,否则都是失败状态
- 一般用来多个接口同时调用时,有一个接口调用失败,都返回失败
let p = Promise.resolve(100) //成功状态 let p2 = new Promise((resolve, reject) => { resolve('res') }) //成功状态 let p3 = Promise.resolve('hello') //成功状态 let result = Promise.all([p, p2, p3]) console.log(result) //输出:Promise {<pending>} 成功状态 result.then(res => { console.log(res) //输出:[100, 'res', 'hello'] })
3-4. Promise.race([xxx])
- 作用:数组包裹n个promise对象,返回一个新的promise对象
- 通俗来说,就是n个promise对象谁先有结果,不管成功/失败,就采用第一个 promise 的值作为返回值
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('one') }, 1000); }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('two') }, 500); }) let result = Promise.race([p1, p2]) console.log(result) //输出:Promise {<pending>} 成功状态 result.then(res => { console.log(res) //输出:two })
三、Promise关键问题
1. 修改Promise对象状态的方法
- resolve() --> pending=>fulfilled
- reject() --> pending=>rejected
- throw xxx 抛出错误 --> pending=>rejected
2. 一个promise指定多个成功/失败回调函数,当状态改变,都会调用
let p = new Promise((resolve, reject) => {
resolve('ok')
});
p.then(res => {
console.log(1, res)
})
p.then(res => {
console.log(2, res)
})
//输出:1 'ok'
//输出:2 'ok'
3. 改变状态和指定回调的顺序问题(了解)
- 都有可能,正常情况是先指定回调函数,再改变状态;也可以先改变状态,再指定回调
注意:指定回调函数和执行回调函数是不一样的
- 先改变状态,再指定回调
- 在执行器中直接调用resolve( )/reject( ),同步任务中调用
- 延迟更长的时间调用then( )
- 什么时候获得数据
- 不管then方法是否先指定了回调函数,必须等状态改变后,才能执行回调函数获得数据
let p = new Promise((resolve, reject) => {
//执行器中如果是异步任务中调用resolve/reject,那么then方法先指定回调,状态后改变
setTimeout(() => {
resolve('ok')
}, 1000);
//执行器中如果是同步任务调用resolve/reject,就是先改变状态,then方法再指定回调
resolve('ok')
});
//then方法指定了回调函数,但是也得等状态改变,才能执行回调函数,才能获得数据
p.then(res => {
console.log(1, res)
},err=>{
console.log(1, err)
})
4. then( )返回的新promise对象的结果状态由什么决定?
由then方法指定的回调函数执行结果决定
- 如果抛出错误,返回的新promise对象状态为rejected
- 如果回调函数返回的是非promise对象,返回的新promise对象状态为resolved
- 如果回调函数返回的是promise对象,返回的新promise对象状态由该promise对象的状态决定
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok') }, 1000); }); let result = p.then(res => { //1.throw,状态结果为rejected // throw '抛出错误' //2.非promise对象,状态结果为fulfilled // return 123 //3.promise对象,由此状态结果决定 // return Promise.resolve('123') //状态结果fulfilled return Promise.reject('123') //状态结果rejected }) console.log(result)
5. promise串联多个任务
promise的then()方法返回的是一个新的promise对象,因此可以使用链式调用,通过then方法的链式调用,串联多个同步/异步任务
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000);
});
//先输出 ok ,然后输出success
p.then(res => {
console.log(res)
return Promise.resolve('success')
}).then(res => {
console.log(res)
})
//先输出 ok ,然后输出undefined
p.then(res => {
console.log(res)
}).then(res => {
console.log(res)
})
//因为第一个then方法的回调函数的返回值是undefined,第二个then方法获取到的值就是undefined
6. promise链式调用异常穿透
- 当使用then链式调用时,可以在最后指定失败的回调
- 前面有任何出了异常,都会传递到最后的失败回调中处理
let p = new Promise((resolve) => { resolve('one') }) p.then(res => { console.log('two') return Promise.reject('失败') }).then(res => { console.log('three') }).then(res => { console.log('four') }).catch(err => { console.log(err) }) //输出 two 失败 //当promise链式调用时,其中有错误抛出或则是失败,后面的then不会执行,直接传递到catch指定的回调函数
7. 中断promise链式调用
- 当使用promise链式调用时,需要中断,不再执行后面回调函数,在回调函数中返回一个pending状态的promise对象
let p = new Promise((resolve) => { resolve('one') }) p.then(res => { console.log('two') return new Promise(() => { }) //回调函数中返回一个pending状态的promise对象,后续就会中断 }).then(res => { console.log('three') }).then(res => { console.log('four') }).catch(err => { console.log(err) })
四、async和await
1. async函数
- async函数返回值为promise对象,和then方法返回promise对象一致
- 返回得promise对象的结果由async函数的返回值决定
- 如果返回值是非promise类型的数据,则async函数返回是成功状态的promise对象
- 如果返回值是promise对象,则该promise对象状态结果决定async函数返回的promise对象
- 如果是抛出错误,async函数返回的promise对象是失败状态
async function main(){ //1.如果返回值是非promise类型的数据,则async函数返回是成功状态的promise对象 //return 100 //2.如果返回值是promise对象,则该promise对象状态结果决定async函数返回的promise对象 //return Promise.resolve('成功') //return Promise.reject('失败') //3.如果是抛出错误,async函数返回的promise对象是失败状态 // throw '抛出错误' } let result = main()
2. await表达式
- await表达式右侧一般是promise对象,也可以是其他值
- 如果右侧是promise对象,await返回的是promise成功的值,如果是失败状态,直接抛出错误
- 如果右侧为非promise对象,则直接返回该值
- 注意:
- await必须写在async函数中,但是async函数可以没有await
- 如果await右侧是失败状态的promise对象,则需要try···catch捕获抛出的错误
async function main() {
//1.如果await右侧是非promise类型的数据,直接返回该值
let res = await 123
console.log(res) //输出123
//2.如果await右侧是promise对象
//如果是成功状态的promise对象
let res1 = await Promise.resolve('成功')
console.log(res1) //输出 成功
//如果是失败状态的promise对象
try {
let res2 = await Promise.reject('失败')
} catch (error) {
console.log(error) //输出失败
}
}
let result = main()
3. async和await结合使用
//我的注意使用途径就是调接口
async function main() {
try {
let res = await 调接口
let res2 = await 调接口
//成功了,处理数据
} catch (error) {
//失败了,处理数据
}
}
main()