Promise详解

ES6 新增 Promise

介绍

Promise 是异步编程的一种解决方案:

从语法上讲,promise是一个对象,从它可以获取异步操作的消息;

从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。

1.为什么需要有Promise?

promise主要解决的问题:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

补充一下,什么是回调函数?

就是我给你传一个函数,你反过来调用我

image.png

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。

// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
if (a > 10 && typeof fn2 == 'function') {
fn2() // 回调函数
}
}
fn1(11, function() {
console.log('this is a callback')
})

2.Promise是什么?怎么使用?

  • Promise 是一个类,字面意思:承诺、期约

  • 通过 new 可以创建一个 Promise 对象,并且需要传入回调函数(executor)

    这个回调函数会被立即执行,并且会传入另外两个回调函数 resolve、reject

    当我们调用resolve回调函数时,会执行Promise对象的then方法传入的第一个回调函数,当调用reject的时候,会执行第二个回调

const promise = new Promise((resolve, reject) => {
resolve("success message")
// reject("failure")
})

promise.then(res => {
console.log(res);
}, err => {
console.log(err);
})

Promise的三种状态

Promise使用过程,我们可以将它划分成三个状态:

pending:待定,初始状态,既没有被兑现,也没有被拒绝;当执行executor中的代码时,处于该状态;

fulfilled:已兑现, 意味着操作成功完成;执行了resolve时,处于该状态; (也有的地方叫resolved状态)

rejected:已拒绝,意味着操作失败; 执行了reject时,处于该状态;

new Promise((resolve, reject) => {
// pending待定

resolve() // fulfilled(已完成)

reject() // rejected(已拒绝)
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})

注意:Promise的状态一旦确定,就不可更改,只有两种状态改变:

pending ->fulfilled pending ->rejected

另外我们如果抛出异常,状态也是rejected,会回调then的第二个参数

new Promise((resolve, reject) => {
throw new Error("异常")
}).then(res => {
console.log(res);
}, err => {
console.log(err); // Error: 异常
})

resolve不同值的区别

resolve(参数):resolve的参数,三种情况

  1. 参数是普通值(数值/字符串/普通对象/undefined),状态变化 pending -> fulfilled

  2. 参数又是一个Promise对象,那么这个新Promise会决定原Promise的状态

    const newPromise = new Promise((resolve, reject) => {
    resolve("hahhaha")
    // reject()
    })

    new Promise((resolve, reject) => {
    resolve(newPromise) // 状态由newPromise决定
    }).then(res => {
    console.log(res); // hahhaha
    }, err => {
    console.log(err);
    })
  3. 参数是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据 then方法的结果来决定Promise的状态

    new Promise((resolve, reject) => {
    const obj = {
    then: function(resolve, reject) {
    resolve("resolve message")
    // reject()
    }
    }
    resolve(obj)

    }).then(res => {
    console.log(res);
    }, err => {
    console.log(err);
    })

Promise对象方法

then,catch,finally

then方法

1. 两个参数

then方法是Promise对象上的方法,实际上是放在Promise的原型上的:Promise.prototype.then

then方法接收两个参数:

  • fulfilled的回调函数:当状态变成fulfilled时会回调的函数
  • reject的回调函数:当状态变成reject时会回调的函数

2. 多次调用

同一个Promise是可以被多次调用then方法的,当resolve方法被回调时,所有的then方法传入的回调函数都会被调用

const promise = new Promise((resolve, reject) => {
resolve("success")
})

// 注意这不是链式调用
promise.then(res1 => {
console.log(res1); // success
})

promise.then(res2 => {
console.log(res2); // success
})

promise.then(res3 => {
console.log(res3); // success
})

3. then的返回值

then方法本身是有返回值的,它的返回值是Promise

  • 如果我们then的第一个回调返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值会被作为一个新的Promise的resolve值
const promise = new Promise((resolve, reject) => {
resolve("success")
})

let newPromise = promise.then(res => {
return "aaa"
})
console.log(newPromise);
image.png

所以我们可以进行链式调用

promise.then(res => {
return "aaa"
}).then(res => {
console.log(res); // "aaa"
return "bbb"
}).then(res => {
console.log(res); // "bbb"
})
  • 如果返回的是一个Promise

那么这个Promise会决定下一个then返回的promise的状态

promise.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1111)
}, 3000)
// reject('err')
})
}).then(res => {
console.log(res); // 3秒后打印 1111
}, err => {
console.log(err);
})
  • 如果返回的是一个对象, 并且该对象实现了then方法
promise.then(res => {
return {
then: function(resolve, reject) {
resolve(2222)
}
}
}).then(res => {
console.log(res) // 222
})

其实一样的,既然then返回一个promise,那就跟前面一样的处理,也就是可以继续链式调用then去处理,自己多尝试各种情况看看就都可以理解了

catch方法

catch方法也是Promise对象上的一个方法:它也是放在Promise的原型上的 Promise.prototype.catch()

  • catch方法传入错误(拒绝)捕获的回调函数
  • catch也可以多次调用
  • 我们可以把catch方法理解为 then 方法的语法糖
const promise = new Promise((resolve, reject) => {
reject("reject message")
})

promise.catch(err => {
console.log(err);
})

// 等价于
promise.then(undefined, err => {
console.log(err);
})
  • 注意下面两种情况,catch的处理
const promise = new Promise((resolve, reject) => {
reject("err message")
// 如果这里调用reject,也就是这个promise的状态是rejected
})

promise.then(res => {
return new Promise((resolve, reject) => {
reject("reject2 message")
})
}).catch(err => {
console.log(err); // err message
})
const promise = new Promise((resolve, reject) => {
resolve("aaa") // 如果这里调用resolve,fulfilled状态
})

promise.then(res => { // 先调用 then 的第一个回调
return new Promise((resolve, reject) => {
reject("reject2 message")
// then返回的promise的状态是rejected
})
}).catch(err => {
console.log(err); // reject2 message catch可以捕获到
})

所以说catch会处理首次出现拒绝状态的Promise

  • catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法
const promise = new Promise((resolve, reject) => {
reject("111")
})

promise.then(res=>{
console.log(res);
}).catch(err => {
console.log(err); // "111"
return "222" // 这个catch返回的promise的状态为 fulfilled
}).then(res=>{
console.log(res); // 222
}).catch(err => {
console.log(err);
})

finally方法

finally是在ES9(ES2018)中新增的一个特性:无论Promise对象变成fulfilled还是reject状态,最终都会被执行,所以finally也不需要接收参数

promise.then(res=>{
console.log(res);
}).catch(err => {
console.log(err);
}).finally(() => {
console.log("finally"); // "finally"
})

finally 也会返回一个promise,但是一般我们不会继续再后面做处理了

Promise类方法

也就是直接通过Promise调用的方法,不需要创建实例

Promise.resolve

直接把某个内容转成Promise来使用

Promise.resolve的用法相当于new Promise,并且执行resolve操作

Promise.resolve("hello")

// 等价于
new Promise((resolve) => {
resolve("hello")
})

resolve的参数跟前面一样的三种,普通的值,Promise,有then方法的obj

Promise.reject

会将Promise对象的状态设置为reject状态

Promise.reject("hello")

// 等价于
new Promise((resolve, reject) => {
reject("hello")
})

注意:Promise.reject无论传入的参数是什么形态,都是会直接作为reject状态的参数

const promise = Promise.reject(new Promise((resolve, reject) => {
resolve("123")
}))

promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err) // err: Promise {<fulfilled>: '123'}
})

Promise.all

将多个Promise包裹在一起形成一个新的Promise,新的Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态变成 fulfilled 状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组

    const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(111)
    }, 1000)
    })
    const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(222)
    }, 2000)
    })
    const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(333)
    }, 3000)
    })

    Promise.all([p1, p2, p3]).then(res => {
    console.log(res); // [ 111, 222, 333 ]
    }).catch(err => {
    console.log(err);
    })
  • 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数

    ...
    const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
    reject(222)
    }, 2000)
    })
    ...

    Promise.all([p1, p2, p3]).then(res => {
    console.log(res);
    }).catch(err => {
    console.log(err); // 222
    })

Promise.allSettled

ES11(ES2022)新增

该方法会在所有的Promise都有结果(无论是fulfilled还是reject)后才会有最终的状态,并且返回的Promise的状态一定是fulfilled

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(222)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})

Promise.allSettled([p1, p2, p3]).then(res => {
console.log("res:", res); // 执行的是这里
}).catch(err => {
console.log("err:", err);
})
image.png

打印的结果是一个数组,存放每一个Promise的结果

status:状态,value:值

Promise.race

race:竞赛,这个方法可以理解为Promise的竞赛

只要有一个Promise变成fulfilled状态, 那么就结束,并使用这个promise的结果

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(222)
}, 500) // 先执行完
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})

Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res);
}).catch(err => {
console.log("err:", err); // err: 222
})

Promise.any

ES12新增,和race方法类似

any方法会等到第一个fulfilled状态,才会决定新Promise的状态

如果所有的Promise都是reject的,那么会报一个AggregateError的错误

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(222)
}, 2000)
})

Promise.any([p1, p2]).then(res => {
console.log("res:", res);
}).catch(err => {
console.log("err:", err); // err: 222
})
// 输出:
// err: AggregateError: All promises were rejected
// err.errors可以拿到全部错误信息