Promise (承诺)是 ES6 引入的异步操作的一种解决方案。Promise 是一个类,用于封装异步操作并可以获取其成功或失败的结果。用同步的方式去写异步代码,链式调用相比于层层嵌套来说代码变得扁平了,解决了之前回调地狱的问题,但是还是需要维护一条调用链。
Promise 解决的不是回调函数的问题,而是层层嵌套的回调函数形成的回调地狱的问题。
传统异步操作的解决方案:
传统异步操作的解决方案,大部分是用回调函数或事件驱动。
- 回调函数:一个函数被作为参数传递到另一个函数里,在那个函数执行完后再执行。
function fn(callback) { setTimeout(() => { let total = 0 for(let i = 0; i <= 10; i++) { total += i } // 2. 传入的回调函数在异步操作执行完之后调用 callback(total) }, 2000) } // 1. 传入一个回调函数, fn((total) => { console.log(total) })// 层层嵌套的回调函数,形成了回调地狱 ajax(url,{ //获取token ajax(url,{ //获取用户信息 ajax(url,{ //获取用户相关新闻 ... }) }) }) - 事件驱动:任务的执行不取决于代码的顺序,而取决于某一个事件是否发生。
Promise 的优缺点:
- 优点:
- 有了 Promise,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
- Promise 提供统一的接口,使得控制异步操作更加容易。
- 缺点:
- 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
创建 Promise 实例对象:
创建 Promise 实例对象需要通过调用 Promise 类。Promise 类接受一个回调函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们也是两个函数。resolve 函数的作用是将 Promise 的状态从进行中变为已完成,在异步操作成功时调用,并可传递参数出去;reject 函数的作用是将 Promise 的状态从进行中变为已失败,在异步操作失败时调用,并可传递参数出去。
// 1. 通过 new Promise() 创建一个 Promise 实例对象
const p = new Promise((resolve, reject) => {
// 2. 传入一个回调函数作为参数,在此回调函数中执行异步操作。异步操作执行成功,调用 resolve() 方法通知 Promise 实例对象,then 方法的回调函数会被立即执行;异步操作执行失败,调用 reject() 方法通知 Promise 实例对象,catch 方法的回调函数会被立即执行
setTimeout(() => {
resolve('成功')
}, 2000)
})
// 3. 调用 Promise 实例对象的 then 方法并传入一个回调函数,传入的回调函数会在调用 resolve() 后立即执行;调用 Promise 实例对象的 catch 方法并传入一个回调函数,传入的回调函数会在调用 reject() 后立即执行
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
创建 Promise 实例对象时,传入的回调函数会被立即执行。
const p = new Promise((resolve, reject) => {
console.log('1')
resolve()
});
p.then(() => {
console.log('2')
});
console.log('3')
// 1
// 3
// 2
resolve() 方法的参数:
- 当
resolve()没有参数时,then()方法中接收到的参数就是 undefined。const p = new Promise((resolve, reject) => { resolve() }) p.then(res => { console.log(res) // undefined }) - 当
resolve()的参数是一个普通值时(非 Promise 类型,且不是 thenable 对象),then()方法中接收到的参数就是原封不动的普通值。const p = new Promise((resolve, reject) => { resolve('成功') }) p.then(res => { console.log(res) // 成功 }) - 当
resolve()的参数本身就是一个 Promise 对象时,当前 Promise 对象的状态会由传入的 Promise 对象的状态决定,then()方法中接收到的参数也会由传入的 Promise 对象决定。const paramsP = new Promise((resolve, reject) => { resolve('成功') }) const p = new Promise((resolve, reject) => { resolve(paramsP) }) p.then(res => { console.log(res) // 成功 }) - 当
resolve()的参数是一个 thenable 对象时(带有 then 方法的对象),会立即执行该对象的 then 方法,并且根据 then 方法的结果来决定当前 Promise 对象的状态和参数。const thenable = { then: () => { // 会打印 thenable。但因为这个 then 方法中既没有调用 resolve(),也没有调用 reject(),因此 p.then() 和 p.catch() 中的回调函数都不会被执行 console.log('thenable') // thenable } } const p = new Promise((resolve, reject) => { resolve(thenable) }) p.then(res => { console.log(res) }).catch(err => { console.log(err) })const thenable = { then: (resolve, reject) => { // 会打印 thenable。因为这个 then 方法中调用了 reject(),因此 p.catch() 中的回调函数会被执行 console.log('thenable') // thenable reject('失败') } } const p = new Promise((resolve, reject) => { resolve(thenable) }) p.then(res => { console.log(res) }).catch(err => { console.log(err) // 失败 })
reject() 方法的参数:
传入的参数无论是什么形态,都会直接作为已失败状态的参数传递到 catch() 方法的回调函数中。
Promise 的状态:
Promise 有三种状态:pending 进行中、fulfilled 已完成、rejected 已失败。Promise 状态的改变只有两种:调用 resolve() 后,由 pending 变为 fulfilled;调用 reject() 后,由 pending 变为 rejected。
const p = new Promise((resolve, reject) => {
//1. 此时,p 的状态是 pending
// 2. 如果调用 resolve(),p 的状态变为 fulfilled
resolve()
// 3. 如果调用 reject(),p 的状态变为 rejected
reject()
})
Promise 的状态不受外界的影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。并且 Promise 的状态一旦改变,就不会再变。
const p = new Promise((resolve, reject) => {
// 调用了两次 resolve(),但是 then() 方法中的回调函数还是只会执行一次。因为第一次调用 resolve(),p 的状态就已经改变了,不会再变,也就不会再触发 then 方法中回调函数的执行
resolve()
resolve()
})
p.then(res => {
console.log('成功')
}).catch(err => {
console.log('失败')
})
const p = new Promise((resolve, reject) => {
// 调用了一次 resolve(),一次 reject(),但是只有 then() 方法中的回调函数会被执行一次。因为第一次调用 resolve(),p 的状态就已经改变了,不会再变,也就不会再触发 catch 方法中回调函数的执行
resolve()
reject()
})
p.then(res => {
console.log('成功')
}).catch(err => {
console.log('失败')
})
Promise 的实例方法:
then() 方法:
then() 方法接收两个回调函数作为参数,这两个函数都接受 Promise 传出的值作为参数。第一个回调函数会在调用 resolve() 方法,Promie 对象的状态变为已完成后立即执行,第二个回调函数会在调用 reject() 方法,Promie 对象的状态变为已失败后立即执行,其中,第二个函数是可选的。
// 创建一个 Promise 实例对象
const p = new Promise((resolve, reject) => {
// 执行异步操作,成功调用 resolve(),失败调用 reject()
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(res=>{
console.log(res) // 成功
}, err=>{
console.log(err)
});
可以多次通过 Promise 实例对象调用 then 方法来多次捕获 Promise 状态的改变。
const p = new Promise((resolve, reject) => {
resolve('成功')
})
// 两个 then 方法中的回调函数都会被执行
p.then(res => {
console.log(res) // 成功
})
p.then(res => {
console.log(res) // 成功
})
then() 方法的返回值:
then() 方法本身是有返回值的,执行后会返回一个新的 Promise 对象,所以可以链式调用。
p.then(res=>{
console.log(res)
}).then(res=>{
console.log(res)
})
then() 方法执行后返回的 Promise 对象的状态,由 then 方法中的回调函数的返回值决定,return 后面的值默认会用 Promise 类包装一下。
- 如果回调函数中没有返回值,那么
then()返回的 Promise 对象的状态为已成功,resolve 的值是 undefined。const p1 = new Promise((resolve, reject) => { resolve('p1') }); p1.then(res => { console.log(res) // p1 // 其实相当于 return new Promise(resolve => resolve(undefined)} }).then(res => { console.log(res) // undefined }) - 如果回调函数中返回一个普通值((非 Promise 类型,且不是 thenable 对象)),那么
then()返回的 Promise 对象的状态为已完成,resolve 的值即为返回的值。const p1 = new Promise((resolve, reject) => { resolve('p1') }); p1.then(res => { console.log(res) // p1 // 其实相当于 return new Promise(resolve => resolve('p2')} return 'p2' }).then(res => { console.log(res) // p2 }) - 如果回调函数中返回一个 Promise 类型的值,那么
then()返回的 Promise 对象的状态即为内部 Promise 返回的状态,resolve/reject 的值即为内部 Promise 对象 resolve/reject 的值。const p1 = new Promise((resolve, reject) => { resolve('p1') }) const p2 = new Promise((resolve, reject) => { resolve('p2') }); p1.then(res => { console.log(res) // p1 return p2 }).then(res => { console.log(res) // p2 }) - 如果回调函数返回一个 thenable 对象时(带有 then 方法的对象),会立即执行该对象的 then 方法,并且根据 then 方法的结果来决定返回的 Promise 对象的状态和参数。
const p1 = new Promise((resolve, reject) => { resolve('p1') }) p1.then(res => { console.log(res) // p1 // 返回的 thenable 对象的 then 方法中既没有调用 resolve(),也没有调用 reject(),因此返回的 Promise 对象的状态仍然是进行中 return { then: () => { } } }).then(res => { // 不会被执行 console.log(res) })const p1 = new Promise((resolve, reject) => { resolve('p1') }) p1.then(res => { console.log(res) // p1 return { then: (resolve) => { resolve('p2') } } }).then(res => { console.log(res) // p2 }) - 如果回调函数中抛出错误,那么
then()返回的 Promise 对象的状态为已失败,reject 的值即为抛出的错误。let p1 = new Promise((resolve, reject) => { resolve('p1') }); p1.then(res => { console.log(res) // p1 throw 'p2' }).then(res => { console.log(res) }, err => { console.log(err) // p2 })
catch() 方法:
catch() 方法接收一个回调函数作为参数,该回调函数会在调用 reject() 方法,Promie 对象的状态变为已失败后立即执行,接收 reject() 传出的值作为参数。
const p = new Promise((resolve, reject) => {
reject('失败')
})
p.catch(err=>{
console.log(err) // 失败
})
Promise 的 rejected 状态只要没被 catch() 方法或者 then() 方法的第二个参数捕获过,就会一直向下传递,直到被捕获。
这一点比较特殊,虽然每次调用 then/catch 都会返回一个新的 Promie 对象,但只要 rejected 状态没被捕获过,就会一直向下传递。
const p = new Promise((resolve, reject) => {
reject('失败')
})
p.then().then().then().catch(err => {
console.log(err) // 失败
})
可以多次通过 Promise 实例对象调用 catch() 方法来多次捕获 Promise 状态变为已失败。
const p = new Promise((resolve, reject) => {
reject('失败')
})
// 两个 catch 方法中的回调函数都会被执行
p.catch(err => {
console.log(err) // 失败
})
p.catch(err => {
console.log(err) // 失败
})
catch() 方法的返回值:
catch() 方法也是会返回一个新的 Promise 对象,所以在 catch() 方法后面也可以链式调用。
p.catch(err =>{
console.log(err)
}).then(res =>{
console.log(res)
})
catch() 方法执行后返回的 Promise 对象的状态,由 catch 方法中的回调函数的返回值决定,return 后面的值默认会用 Promise 类包装一下。
和
then()方法的返回值完全一样。
finally() 方法:
finally() 方法是在 ES9 中新增的特性。当 Promise 状态发生变化时,不论如何变化都会执行。finally() 方法接收不到任何参数。
Promise 的静态方法:
Promise.resolve():
Promise.resolve():相当于 new Promise(),并执行 resolve 操作。可传入的参数和 new Promise() 中 resolve 操作的一样。返回一个已完成状态的 Promise 对象。
Promise.resolve(‘成功’)
// 相当于:
new Promise(resolve => resolve('成功'))
Promise.reject():
Promise.reject():相当于 new Promise(),并执行 reject 操作。可传入的参数和 new Promise() 中 reject 操作的一样。返回一个已失败状态的 Promise 对象。
Promise.reject('失败')
// 等价于:
new Promise(reject => reject('失败'))
Promise.all():
Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的 Promise 对象。
Promise.all()、Promise.race()、Promise.allSettled()的参数其实不只是数组,任何可迭代的结构都可作为参数。
参数中若有成员不是 Promise 对象,内部会将其转变成 Promise 对象。
const p = Promise.all([p1,p2,p3])
只有 p1、p2、p3 的状态都变为已完成,p 的状态才会变为已完成,此时 p1、p2、p3 的 resolve 的值会被组成一个数组,作为参数传递给 p 的 then() 方法。
只要 p1、p2、p3 中有一个的状态是已失败,p 的状态就会变为已失败,此时,第一个已失败的实例的 reject 的值,会被作为参数传递给 p 的 catch() 方法。
const p1= Promise.resolve('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.all([p1, p2, p3]).then(res=>{
console.log(res) // ["p1", "p2", "p3"]
})
const p1= Promise.resolve('p1')
const p2= Promise.reject('p2')
const p3= Promise.reject('p3')
Promise.all([p1,p2,p3]).then(res=>{
console.log(res)
}).catch(err => {
console.log(err) // p2
})
Promise.any():
Promise.any():ES12 中新增了 Promise.any() 方法。用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的并且永远是 resolved 状态的 Promise 对象。
const p = Promise.any([p1,p2,p3])
只要 p1、p2、p3 中有一个的状态是已完成,p 的状态就会变为已完成,此时,第一个已完成的实例的 resolve 的值,会被作为参数传递给 p 的 then() 方法。
只有 p1、p2、p3 的状态都变为已失败,p 的状态才会变为已失败,p 的 catch() 方法会接收到一个默认的参数 AggregateError: All promises were rejected。
const p1= Promise.reject('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.any([p1,p2,p3]).then(res=>{
console.log(res) // p2
}).catch(err => {
console.log(err)
})
const p1= Promise.reject('p1')
const p2= Promise.reject('p2')
const p3= Promise.reject('p3')
Promise.any([p1,p2,p3]).then(res=>{
console.log(res)
}).catch(err => {
console.log(err) // AggregateError: All promises were rejected
})
Promise.race():
Promise.race():用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的 Promise 对象。
const p = Promise.race([p1,p2,p3])
只要 p1、p2、p3 中有一个实例率先改变状态,p 的状态就会跟着改变,那个率先改变的 Promise 实例的返回值,会作为参数传递给 p 的后续方法。
const p1= Promise.resolve('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.race([p1,p2,p3]).then(res=>{
console.log(res) // p1
})
const p1= Promise.reject('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.race([p1,p2,p3]).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err) // p1
})
Promise.allSettled():
Promise.allSettled():ES11 中新增了 Promise.allSettled() 方法。用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的并且永远是 resolved 状态的 Promise 对象。
Promise.allSettled()的状态永远都是已完成,与传入的 Promise 对象参数的状态无关。只会记录下每个 Promise 的状态和结果。
const p = Promise.allSettled([p1,p2,p3])
当 p1、p2、p3 全部处理完成后,可以拿到每个 Promise 的状态和结果。
const p1= Promise.resolve('p1')
const p2= Promise.reject('p2')
const p3= Promise.resolve('p3')
Promise.allSettled([p1,p2,p3]).then(res=>{
console.log(res)
})

5995






