Promise
1.异步处理代码的困境
不用Promise处理异步代码,在ES6之前处理异步代码的一般方式。
1 | function execCode(count, successCallback, failureCallback) { |
设计处理异步代码函数的人,设计的方案可能有多种方法,比如函数名的不同、传递参数的顺序等
当使用这个函数的人去使用这个处理异步代码函数的时候,不知道函数名的命名规范,不知道传递参数的顺序,很容易出现问题,就像“一千个人有一千个哈姆雷特”,很难统一规范,虽然随着时间的推移,在社区中也会慢慢的去统一这种规范,但是需要花费很长的时间的演变。
在ES6之后推出了Promise,为了解决这种困境。
2.Promise处理异步代码
我们从代码量上来看,两者好像都差不多,主要思想也都是差不多的,参数,回调函数这些东西,但是Promise在execCode函数的执行,传入参数上来说,所传的参数不同,前者需要传入参数,以及成功和失败的回调函数,后者(Promise)只需要传入参数即可,回调函数它内部或是底层帮我们完成了,我们只需要按照统一的规范去编写代码就可以了,不用担心哪里会有问题。不用担心规范的问题。
1 | function execCode(total) { |
3.Promise的状态
- pending:待定状态(没有兑现,没有拒绝)
- fulfilled:兑现状态(已经兑现)
- rejected:拒绝状态(已经拒绝)
Promise的状态一旦从pending转变到另一状态(pending=> fulfilled/pending => rejected),那么状态就不会在改变,即使再次执行其他的回调函数也不能改变状态。
1 | const promise = new Promise((resolve, reject) => { |
Promise的resolve/reject
1.resolve的参数
普通值(数组/对象/字符串…)
resolve(promise)
这里传入的新的promise,那么新的promise的会决定原有promise的状态。
1
2const newPromise = new Promise(reject => reject())
const oldPromise = new Promise(resolve => resolve(newPromise))也就是说oldPromise原本的状态是从pending(待定状态)转变成fulfilled(已兑现的状态),但是由于resolve接收的参数是一个promise对象,那么oldPromise的状态会编程newPromise的状态,也就是oldPromise的状态编程了已拒绝的状态(rejected。
如果resolve传入的对象实现了then方法,那么会执行该then方法,并根据then方法的返回结果来决定Promise的状态
1
2
3
4
5
6
7const promise = new Promise(resolve => resolve({
name: 'beichen',
then(resolve) {
resolve('111')
}
}))
promise.then(res => console.log(res)) // 111
2.promise的then方法的参数
1 | const promise = new Promise((resolve, reject) => { |
3.Promise.then的返回值是一个promise对象
1 | const promise = new Promise((resolve, reject) => { |
4.Promise.catch的返回值
1 | const promise = new Promise((resolve, reject) => { |
最最后加的catch捕获错误,并不是加到第三个then返回的promise对象上的,如果想要执行catch函数,捕获到错误,第一种方式就是在new Promise((resolve, reject) => reject())
这里调用reject回调函数,他不会进入到then里面,而是找到当前promise调用的第一个catch,无论前面写了多少个then,都不会执行。第二种就是通过resolve进入到then里面,然后在then中抛出异常,终端函数的执行,promise会捕获异常,直接catch函数。
5.finally的回调
无论promise的状态是fulfilled还是rejected的状态,最终都会执行finally函数,且不接收参数。
Promise的类方法
resolve/reject方法
1
2Promise.resolve("成功")
Promise.reject("失败")all方法
其中只要有一个拒绝状态就回调Promise.all().catch(err=> {})
当all()里面的promise对象的决议都为fulfilled的时候,会将这两个promise的决议结果存放在数组中,并调用Promise.all().then()方法。如果决议在定时器中拿到结果,那么也会等所有的resolve决议结束后在执行all.then方法,前提是所有决议中没有reject()。否则会直接执行all.catch()。
1
2
3
4
5
6
7
8
9
10
11
12const p1 = new Promise((resolve, reject) => {
resolve("p1 resolve")
})
const p2 = new Promise((resolve, reject) => {
// resolve("p2 resolve")
reject("p2 reject")
})
Promise.all([p1, p2]).then(res => {
// console.log(res) // ["p1 resolve", "p2 resolve"]
}).catch(err => {
console.log(err) // p2 reject
})当all()里面有一个promise对象的决议为rejected的时候,就会将这个拒绝的决议的结果付给Promise.all().catch(err)的参数err。
allSettled方法
所有的Promise都有结果时返回,并且结果包含对应promise的决议状态和返回的结果,返回一个数组,数组中用对象存储决议状态和结果
该方法会在所有的promise结果都返回的时候执行,无论决议结果是fulfilled还是rejected,并一定执行
Promise.allSettled([p1, p2]).then(res => console.log(res))
,返回的结果包含对应promise的决议状态和返回的结果。race方法
谁先拿到结果就返回谁。race([])传入一个数组
谁有结果就先用谁的,无论决议结果如何。
Promise.race([p1, p2]).then(res => console.log(res)).catch(err => console.log(err))
any方法
谁先有兑现状态就用谁的,否则返回所有的promise都是拒绝状态
谁先有fulfilled的结果就用谁的,如果都没有,那么就返回all promise were rejected。
async/await
async/await就是Promise和generator的语法糖,结合两种方法一起使用
1 | function requestData(url) { |
解释一下执行的顺序:
- 定义了一个requestData的函数,并传入一个参数
- 定义了一个getData的生成器函数
- 调用getData生成器函数,返回一个生成器对象。
- 调用生成器的next()方法获取到的是requestData(‘beichen’)所返回的一个对象,这个对象里面的value的值是一个Promise对象,因为在requestData中的定时器中返回的promise的状态是fulfilled,所以我们继续调用promise对象的then方法,获取到resolve(url)返回的值,并打印res1。得到beichen。
- 然后继续调用生成器的next()方法,并把res 也就是beichen字符串传递给getData生成器函数中的res1,第一个yield前面的函数得到返回值res(‘beichen’),并继续执行第二个yield后面的函数,参数进行拼接,继续重复上一步的操作。
- 拿到res2的结果,继续将res2作为参数传递给下一个yield前面的res,继续执行。最后打印得到res3的结果。
- 如果继续执行生成器的next函数,那么会得到
{ done: true, value: undefined }
获取不到value的值,所以继续执行也就没有什么意义了。
接下来就是async/await的简便方法(语法糖)执行。
1 | function requestData(url) { |
从代码上来看,简洁了很多,从结构上来看,更加的清晰。