一、Promise 简介
1. 对 Promise 的理解
Promise 是 JS 中进行异步编程的新的解决方案。
- 从语法上看,Promise 是一个构造函数,使用
new关键字,可以得到一个 promise 实例对象 - 从功能上看,promise 对象可以用来封装异步操作,通过
then()可以获取其成功或失败的结果
2. promise 的状态与值
const p = new Promise(() => {})
console.log(p)
2.1 promise 的状态(PromiseState)
promise 有三个状态:pending、resolved、rejected。
- 一个 Promise 刚被 new 出来的时候,状态是
pending(未确定的) - 当执行了
resolve(),状态由pending变为resolved(成功) - 当执行了
reject(),状态由pending变为rejected(失败)
注意:promise 的状态只能改变一次,重复修改不起作用。也就是说,一旦状态改变了,状态就固定了(resolved 或 rejected),不会被再次改变。
2.2 promise 的值(PromiseResult)
无论 promise 的状态是成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,失败的结果数据一般称为 reason。
3. Promise 的基本流程
二、Promise 使用
1. Promise 构造函数的基本使用
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve函数的作用是,将 promise 对象的状态从pending变为resolved,在异步操作成功时调用,并将异步操作的结果,作为参数value传递出去;reject函数的作用是,将 promise 对象的状态从pending变为rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason传递出去。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const time = Date.now()
if (time % 2 === 0) { // 当前时间是偶数代表成功,否则代表失败
resolve(1)
} else {
reject(2)
}
}, 2000)
})
2. Promise.prototype.then()
Promise 实例对象生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
then 方法接受两个回调函数作为参数(两个参数都是可选的):
- 第一个回调函数
onResolved(value),是 promise 对象的状态变为resolved时调用 - 第二个回调函数
onRejected(reason),是 promise 对象的状态变为rejected时调用
then 方法的返回值是一个新的 promise 对象,意味着 .then() 之后可以继续 .then()。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const time = Date.now()
if (time % 2 === 0) { // 当前时间是偶数代表成功,否则代表失败
resolve(1)
} else {
reject(2)
}
}, 2000)
})
p.then(value => { // onResolved 函数,value 是 resolve 传递出来的值
console.log(value)
}, reason => { // onRejected 函数,reason 是 reject 传递出来的值
console.log(reason)
}).then(value => { // 此时的 value 是上一个 then 返回的 promise 的成功的值
console.log(value)
}, reason => { // 此时的 reason 是上一个 then 返回的 promise 的失败的值
console.log(reason)
})
3. Promise.prototype.catch()
catch 方法用于指定失败的回调函数,相当于 then 的第二个回调函数 onRejected。
// p 是一个 promise 对象
p.catch(error => {
console.log(error)
})
// 相当于
p.then(null, reason => {
console.log(reason)
})
一般放到 then 之后用于捕获错误。
// p 是一个 promise 对象
p.then(value => {
console.log(value)
throw new Error('出错了')
}, reason => {
console.log(reason)
}).catch(error => { // 当 then 中的逻辑抛出错误,就会被 catch 捕获到,error 就是抛出的错误信息
console.log(error)
})
4. Promise.resolve()
参数:非 Promise 类型的值或 promise 对象。
返回值:
- 如果参数是非 Promise 类型的值,就返回一个成功的 promise 对象
- 如果参数是 promise 对象,就根据这个 promise 的结果,返回一个成功的或失败的 promise 对象
// 参数是非 Promise 类型的值
const p = Promise.resolve(123)
console.log(p)
// 参数是成功的 promise 对象
const promise = new Promise((resolve, reject) => {
resolve(123)
})
const p = Promise.resolve(promise)
console.log(p)
// 参数是失败的 promise 对象
const promise = new Promise((resolve, reject) => {
reject(123)
})
const p = Promise.resolve(promise)
console.log(p)
5. Promise.reject()
参数:非 Promise 类型的值或 promise 对象。
返回值:返回一个失败的 promise。
// 参数是非 Promise 类型的值
const p = Promise.reject(123)
console.log(p)
// 参数是成功的 promise 对象
const promise = new Promise((resolve, reject) => {
resolve(123)
})
const p = Promise.reject(promise)
console.log(p)
// 参数是失败的 promise 对象
const promise = new Promise((resolve, reject) => {
reject(123)
})
const p = Promise.reject(promise)
console.log(p)
6. Promise.all()
参数:包含多个 promise 对象的数组。
返回值:
- 当所有 promise 都成功,就返回一个成功的 promise,promise 的值是一个数组,数组中的元素就是参数数组中所有 promise resolve 出来的值,且数组中值的顺序与参数数组中 promise 的顺序保持一致
- 当有一个 promise 失败,就返回一个失败的 promise,promise 的值就是失败的那个 promise 的值
- 当有多个 promise 失败,就返回一个失败的 promise,promise 的值就是最先失败的那个 promise 的值
// 所有 promise 都成功
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const pAll = Promise.all([p1, p2, p3])
console.log('pAll:', pAll)
// 有一个 promise 失败
const p1 = Promise.resolve(1)
const p2 = Promise.reject(2)
const p3 = Promise.resolve(3)
const pAll = Promise.all([p1, p2, p3])
console.log('pAll:', pAll)
// 有多个 promise 失败
const p1 = Promise.resolve(1)
const p2 = Promise.reject(2)
const p3 = Promise.reject(3)
const pAll = Promise.all([p1, p3, p2])
console.log('pAll:', pAll)
7. Promise.race()
参数:包含多个 promise 对象的数组。
返回值:返回一个 promise,promise 的值就是参数数组中最先返回结果(无论成功或失败)的 promise 的值。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
const p2 = Promise.reject(2)
const p3 = Promise.reject(3)
const pRace = Promise.race([p1, p3, p2])
console.log('pRace:', pRace)
三、Promise 的几个关键问题
1. 如何改变 promise 的状态
当执行 new Promise(() => {}) 时,就会返回一个 promise 对象,此时 promise 的状态是 pending。
pending变为resolved:调用resolve()。pending变为rejected:- 调用
reject(); - 抛出异常:
throw new Error。
- 调用
new Promise((resolve, reject) => {
resolve(1) // 状态从 pending 变成 resolved
})
.then(value => { // 调用 then 中的 onResolved 回调
console.log('value:', value)
})
// value: 1
new Promise((resolve, reject) => {
reject(1) // 状态从 pending 变成 rejected
})
.then(value => {
console.log('value:', value)
}, reason => { // 调用 then 中的 onRejected 回调
console.log('reason:', reason)
})
// reason: 1
new Promise((resolve, reject) => {
throw 1 // 状态从 pending 变成 rejected
})
.then(value => {
console.log(value)
}, reason => { // 调用 then 中的 onRejected 回调
console.log('reason:', reason)
})
// reason: 1
2. 一个 promise 指定多个成功/失败回调函数,都会调用吗
当 promise 改变为对应状态时,多个 then 中对应的回调都会调用。
注意,这里不是指多个 then 链式调用。
const p = new Promise((resolve, reject) => {
resolve(1)
})
p.then(value => {
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
})
p.then(value => {
console.log('value2:', value)
}, reason => {
console.log('reason2:', reason)
})
// value1: 1
// value2: 1
3. 改变 promise 状态和指定回调函数谁先谁后
可以先改变状态,后指定回调;也可以先指定回调,后改变状态。
无论是先改变状态后指定回调,还是先指定回调后改变状态,当状态发生改变时,then 中的回调函数都能拿到对应状态的数据。
3.1 先改变状态,后指定回调
const p = new Promise((resolve, reject) => { // 这里的回调是同步回调,会立即执行回调函数中的代码
resolve(1)
})
p.then(value => { // then 中的回调是异步回调,不会立即执行,它会等 promise 状态改变了再执行
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
})
// value1: 1
3.2 先指定回调,后改变状态
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
p.then(value => {
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
})
// value1: 1
4. promise.then() 返回的新 promise 的结果状态由什么决定
返回的新 promise 的状态由 then() 中的回调函数执行的结果决定:
- 如果回调抛出异常,就返回失败的 promise;
- 如果返回的是非 promise 的值,就返回成功的 promise;
- 如果返回的是 promise 对象,此 promise 就会作为新 promise 返回。
// 如果回调抛出异常,就返回失败的 promise
new Promise((resolve, reject) => {
resolve(1)
})
.then(value => {
console.log('value1:', value)
throw value // 抛出异常,返回失败的 promise
}, reason => {
console.log('reason1:', reason)
})
.then(value => {
console.log('value2:', value)
throw value
}, reason => { // 由于上一个 then 返回失败的 promise,所以这里执行 onRejected 回调
console.log('reason2:', reason)
})
// value1: 1
// reason2: 1
// 如果返回的是非 promise 的值,就返回成功的 promise
new Promise((resolve, reject) => {
reject(1)
})
.then(value => {
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
return reason+1 // 正常返回值
})
.then(value => { // 由于上一个 then 没有抛出异常,所以这里执行 onResolved 回调
console.log('value2:', value) // 这里的 value 就是上一个 then 执行的回调
}, reason => {
console.log('reason2:', reason)
})
// reason1: 1
// value2: 2
// 如果返回的是成功的 promise 对象
new Promise((resolve, reject) => {
reject(1)
})
.then(value => {
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
return Promise.resolve(123) // 返回成功的 promise
})
.then(value => { // 由于上一个 then 返回成功的 promise,所以这里执行 onResolved 回调
console.log('value2:', value)
}, reason => {
console.log('reason2:', reason)
})
// reason1: 1
// value2: 123
// 如果返回的是失败的 promise 对象
new Promise((resolve, reject) => {
reject(1)
})
.then(value => {
console.log('value1:', value)
}, reason => {
console.log('reason1:', reason)
return Promise.reject(123) // 返回失败的 promise
})
.then(value => {
console.log('value2:', value)
}, reason => { // 由于上一个 then 返回失败的 promise,所以这里执行 onRejected 回调
console.log('reason2:', reason)
})
// reason1: 1
// reason2: 123
5. promise 如何串联多个操作任务
写法同上(第 4 点),不再赘述。
6. promise 异常传透
当使用 promise 的 then 链式调用时,可以在最后使用 catch 指定失败的回调。前面任何操作出了异常,都会传到最后的 catch 中。
new Promise((resolve, reject) => {
reject(1)
})
.then(value => {
console.log('value1:', value)
return 2
})
.then(value => {
console.log('value2:', value)
return 3
})
.then(value => {
console.log('value3:', value)
})
.catch(error => {
console.log('error:', error)
})
// error: 1
7. 中断 promise 链
当使用 promise 的 then 链式调用时,可以在中间的某个回调函数中返回一个 pending 状态的 promise 对象,调用链就会在这里中断,不再调用后面的回调函数。
new Promise((resolve, reject) => {
reject(1)
})
.then(value => {
console.log('value1:', value)
return 2
})
.then(value => {
console.log('value2:', value)
return 3
})
.then(value => {
console.log('value3:', value)
})
.catch(error => {
console.log('error:', error)
return new Promise(() => {}) // 返回状态为 pending 的 promise
})
.then(value => { // 由于上一个 catch 的回调返回 pending 状态的 promise,所以 then 中的回调不会执行
console.log('value:', value)
})