一、Promise的使用
1.1. 异步代码存在的困境(异步任务的处理)
-
异步代码的概念
- 同步就是代码按照顺序依次执行
- 在js中,我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。
- 现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。
- 为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。
- 为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。
-
例子
- 我们调用一个函数来计算累加和,用定时器模拟异步
- 如果计算成功了,就告诉我成功了,并且把最后的结果给我返回
- 如果出现某些问题失败了,就告诉我失败了,并且把错误信息返回
// 定义一个成功和一个失败的函数 function successFn(res) { console.log("操作成功了", res) } function failureFn(err) { console.log("操作失败了", err) } function foo(num, successFn, failureFn) { let total = 0 setTimeout(() => { if (num > 0) { for (var i = 0; i < num; i++) { total += i } // 利用回调函数返回成功信息 successFn(total) } else { // 利用回调函数返回错误信息 failureFn(`${num}不合法`) } }, 3000) } foo(0, successFn, failureFn)
1.2. Promise的使用
-
使用异步代码确实可以实现计算完累加和后,返回对应的信息,但是它存在两个主要问题
-
我们需要自己设计回调函数、回调函数的使用、回调函数的名称等
-
对于不同的人、不同的框架设计出来的方案是不同的,所以就需要我们去看对方的源码,这样才能了解到这个函数到底怎么用
-
但是如果使用Promise的话,因为它是ECMA的规范,大家使用的都是同样的方法,所以就可以避免以上两个问题
- 在new Promise这个类时,需要同时给传入一个回调函数
- 这个回调函数会接受一个resolve和reject两个参数
- 在这个回调函数中如果调用resolve的话,Promise的实例对象的then方法中的回调函数就会被调用
- 在这个回调函数中如果调用reject的话,Promise的实例对象的catch方法中的回调函数就会被调用
function foo(num) { // 返回Promise的实例对象 return new Promise((resolve, reject) => { let total =0 setTimeout(() => { if (num > 0) { for (var i = 0; i < num; i++) { total += i } // 利用回调函数返回成功信息 resolve(total) } else { // 利用回调函数返回错误信息 reject(`${num}不合法`) } }, 3000) }) } // 接收Promise的实例对象 let promise = foo(0) // 调用promise的then和catch方法 promise.then(res => { console.log("调用成功的回调函数了", res) }).catch(err => { console.log("调用失败的回调函数了", err) })
-
1.3. Promise的三种状态
-
待定(pending):初始状态,既没有被兑现,也没有被拒绝
-
已兑现(fulfilled):意味着操作成功完成,执行了resolve()时,处于该状态
-
已拒绝(rejected):意外着操作失败,执行了reject()时,处于该状态
let promise = new Promise((resolve, reject) => { // 1. 待定状态 // 2. 已兑现状态 resolve("成功咯") // 3. 已决绝状态 reject("失败咯") }) promise.then(res => { console.log("成功", res) }).catch(err => { console.log("失败", err) })
1.4. Executor
- Executor是创建Promise时,需要传入的一个回调函数,这个函数会被立即执行,并且会传入两个参数resolve和reject
- 注意:
- 状态一旦被确定下来,Promise的状态就会被锁死,该Promise的状态就不能更改了
- 在我们调用resolve时,如果传入的值本身不是一个Promise,那么就将该Promise的状态变成兑现了
- 在之后,我们再调用reject时,就不会有任何的响应了(这并不代表它不会执行后面的代码,只是无法改变Promise的状态)
- 状态一旦被确定下来,Promise的状态就会被锁死,该Promise的状态就不能更改了
1.5. 以下知识点均为了解即可
1.5.1. resolve不同值的区别
-
如果resolve传入的是一个普通的值或者对象,那么这个值就会作为then回调函数的参数
-
如果resolve中传入的是另一个Promise,那么这个新的Promise就会决定原Promise的状态
-
如果resolve中传入的是一个对象,并且这个对象中实现了then方法(又称为thenable),那么就会执行then方法,并且根据then方法的结果来决定原Promise的状态
-
// 新的Promise对象 let promise1 = new Promise((resolve, reject) => { reject() }) // 有then方法的对象 let obj = { name: "Judy", age: 18, then: function(resolve, reject) { resolve() } } let promise = new Promise((resolve, reject) => { // 1. 传入普通的值或者对象 resolve("成功咯") // 2. 传入一个新的Promise resolve(promise1) // 3. 传入一个有then方法的对象 resolve(obj) }) promise.then(res => { console.log("成功") }).catch(err => { console.log("失败") })
1.2. then方法-接收两个参数
let promise = new Promise((resolve, reject) => {
resolve()
// reject()
})
// then方法中可以有两个参数,并且是可以多次调用的
promise.then(res => {
console.log("成功")
}, err => {
console.log("失败")
})
1.5.3. then和catch都可以多次调用
let promise = new Promise((resolve, reject) => {
// resolve()
reject("失败咯")
})
// Uncaught (in promise) 失败咯
// 会报这个错是因为,第一个这个promise默认也是会监听拒绝的
// 只不过我们没有定义出来拒绝的状态下,应该做什么操作,所以就会报那个错
promise.then(res => {
console.log("成功")
})
promise.then(res => {
console.log("成功")
})
promise.catch(err => {
console.log("失败", err)
})
promise.catch(err => {
console.log("失败", err)
})
1.5.4. then方法的返回值
-
then方法本身是有返回值的,它又会返回一个Promise包裹了某种状态(如Promise.resolve(return返回的值)/Promise.reject(return返回的值)),所以可以如下进行链式调用
- 当then方法中的回调函数正在执行时,返回的Promise处于pending状态
- 当then方法中的回调函数返回值时,它的状态就会改变
- 返回一个普通值,那Promise就处于fulfilled状态
- 返回新的Promise时,就由新Promise决定它的状态
- 返回一个对象,对象中有then方法时,就由then方法中决定它的状态
- 当then方法中抛出一个异常时,就会处于rejected状态
- 如果不返回值或者抛出异常时,默认返回undefined。如果then方法后面还有then在监听的话,那么就会继续监听到resolve,只不过传过来的值就是返回的undefined
const promise = new Promise((resolve, reject) => { resolve("aaaa") }) // 监听Promise的状态 promise.then(res => { console.log("第一次then调用", res) // 1. 返回一个普通值,处于fulfilled状态 return "Judy" // 2. 返回一个thenable或者新的Promise return new Promise((resolve, reject) => { resolve("新Promise的resolve") reject("新Promise的reject") }) return { then: function(resolve, reject) { resolve("thenable中的resolve") reject("thenable中的reject") } } // 3. 抛出异常,处于rejected状态 throw new Error("error了") }).then(res => { console.log("第二次then调用", res) }).then(res => { console.log("第三次then调用", res) }).catch(err => { console.log("第一次catch", err) })
1.5.5. catch方法的返回值
-
catch方法也会返回一个新的Promise,所以也可以继续在catch后面调用then和catch方法
-
catch方法传入的回调函数在执行完后,返回的新Promise的状态依旧是fulfilled
-
如果想要后面继续执行catch,就需要在第一个catch中,抛出一个异常
const promise = new Promise((resolve, reject) => { reject("aaaa") }) // 监听Promise的状态 promise.then(res => { console.log("第一次then调用", res) }).catch(err => { console.log("第一次catch", err) // 如果想要继续调用catch,就得抛出一个异常 throw new Error("catch中抛出的异常") }).catch(err => { console.log("第二次catch", err) }).then(res => { console.log("第二次then调用", res) })
1.5.6. finally方法的使用
-
表示无论Promise的状态时fulfilled还是rejected,finally中的代码都会执行
const promise = new Promise((resolve, reject) => { resolve("aaaa") // reject("bbbb") }) // 监听Promise的状态 promise.then(res => { console.log("then调用", res) }).catch(err => { console.log("catch", err) }).finally(() => { console.log("我是finally中的代码") })
1.5.7. Promise类方法的使用
-
resolve类方法的使用
-
有时候我们已经有一个现成的内容了,只是希望把它转成Promise来使用,就可以使用resolve这个类方法
-
Promise.resolve()的用法就相当于new Promise,并且执行了resolve()操作
-
同样的这个resolve中的参数也有三种形态:
-
普通值
-
新的Promise对象
-
thenable
下面案例中的的promise1和promise2的作用是相等的
const promise1 = new Promise((resolve, reject) => { resolve("Judy") }) const promise2 = Promise.resolve("Judy") promise2.then(res => { console.log("then方法中回调函数的调用") })
-
-
-
-
reject类方法的使用
-
reject这个类方法的使用类似于resolve类方法
-
Promise.reject()的用法就相当于new Promise,并且执行了reject()操作
-
这个reject中无论传入的参数是什么形态,都会作为reject状态的参数传递到catch上(如果忘了可以自己试试)
// const promise1 = new Promise((resolve, reject) => { // reject("Judy") // }) const promise2 = Promise.reject("Judy") promise2.then(res => { console.log("then方法中回调函数的调用") }).catch(err => { console.log("catch方法中回调函数的调用") })
-
-
all方法
-
它的作用是将多个Promise包裹在一起,形成一个新的Promise
-
新的Promise的状态由包裹在一起的Promise共同决定
- 当所有的Promise状态都为fulfilled时,新Promise的状态也为fulfilled,并且会将所有Promise的返回值组成一个数组作为then方法的参数
- 当有一个Promise的状态为rejected时,新Promise的状态也会为rejected,并且会将第一个rejected的返回值作为catch的参数
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise1") reject("promise1 Rejected") }, 3000) }) const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise2") }, 2000) }) const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise3") }) }) // Promise.all中传入的必须是一个可迭代对象 Promise.all([promise1, promise2, promise3]).then(res => { console.log("then方法", res) }).catch(err => { console.log("catch方法", err) })
-
-
allsettled方法
-
all方法有一个缺陷,就是当一个promise的状态为rejected时,新Promise的状态立即就为rejected,这样就无法获得其他的Promise的状态了
-
allsettled方法,会等所有的Promise都有结果之后(无论是fulfilled还是rejected),新Promise才会有状态。返回一个数组作为then方法的参数
[{status: fulfilled/rejected, value: value/reason}]
-
并且这个新Promise的状态一定是fulfilled
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise1") reject("promise1 Rejected") }, 3000) }) const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise2") }, 2000) }) const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise3") }) }) Promise.allSettled([promise1, promise2, promise3]).then(res => { console.log("then方法", res) }).catch(err => { console.log("catch方法", err) })
-
-
race方法
-
race方法就是多个Promise竞争,谁第一个产生状态,就使用谁(无论是fulfilled还是rejected)
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise1") reject("promise1 Rejected") }, 1000) }) const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise2") }, 2000) }) const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("promise3") }, 5000) }) Promise.race([promise1, promise2, promise3]).then(res => { console.log("then方法", res) }).catch(err => { console.log("catch方法", err) })
-
-
any方法
-
any方法和race方法类似,只不过any方法会等到一个fulfilled状态,才会决定新Promise的状态
-
如果所有的Promise的状态都是rejected的话,就会返回一个
AggregateError: All promises were rejected
错误,作为catch的参数const promise1 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise1") reject("promise1 Rejected") }, 1000) }) const promise2 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise2") reject("promise2 Rejected") }, 2000) }) const promise3 = new Promise((resolve, reject) => { setTimeout(() => { // resolve("promise3") reject("promise3 Rejected") }, 5000) }) Promise.any([promise1, promise2, promise3]).then(res => { console.log("then方法", res) }).catch(err => { console.log("catch方法", err) })
-