0


【从用法到手写实现】一文带你深入理解面试之Promise

文章目录

Promise的用法

一、什么是Promise ?

  • Promise是一个类,可以翻译成 承诺、许诺 、期约;
  • 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
  • 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject; 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数; 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;

二、Promise的代码结构

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

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝; 当执行executor中的代码时,处于该状态;
  • 已兑现(fulfilled): 意味着操作成功完成; 执行了resolve时,处于该状态;
  • 已拒绝(rejected): 意味着操作失败; 执行了reject时,处于该状态
newPromise((resolve, reject)=>{// pending状态: 待定的//reject("处于rejected状态") // 处于rejected状态(已拒绝状态)resolve("处于fulfilled状态")// 处于fulfilled状态(已敲定/兑现状态)}).then(res=>{
  console.log("res:", res)//res即为resolve里面传进来的参数,处于fulfilled状态}).catch(err=>{
  console.log("err:", err)//err即为reject里面传进来的参数,处于rejected状态})

Executor是在创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数:
通常我们会在Executor中确定我们的Promise状态:

通过resolve,可以兑现(fulfilled)Promise的状态,我们也可以称之为已决议(resolved);
通过reject,可以拒绝(reject)Promise的状态;

这里需要注意:

  • 一旦状态被确定下来,Promise的状态会被 锁死,该Promise的状态是不可更改的
  • 在我们调用resolve的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成兑现(fulfilled);在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态)

三、Promise的对象方法then、catch、finally

then方法:
then方法是Promise对象上的一个方法:它其实是放在Promise的原型上的 Promise.prototype.then
then方法接受两个参数:

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

一个Promise的then方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的fulfilled回调;
  • 当Promise的状态变成fulfilled的时候,这些回调函数都会被执行;
  • 在这里插入图片描述catch方法: catch方法也是Promise对象上的一个方法:它也是放在Promise的原型上的 Promise.prototype.catch。 一个Promise的catch方法是可以被多次调用的:
  • 每次调用我们都可以传入对应的reject回调;
  • 当Promise的状态变成reject的时候,这些回调函数都会被执行;
promise.catch(err=>{console.log(err)})
promise.catch(err=>{console.log(err)})

finally方法:

  • finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码。
  • finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行
const promise =newPromise((resolve, reject)=>{// resolve("resolve message")reject("reject message")})

promise.then(res=>{
  console.log("res:", res)}).catch(err=>{
  console.log("err:", err)}).finally(()=>{
  console.log("finally code execute")//都会被执行})

四、Promise的类方法resolve、reject、all、allSettled、race、any方法

前面我们学习的then、catch、finally方法都属于Promise的实例方法,都是存放在Promise的prototype上的。
下面我们再来学习一下Promise的类方法:
resolve:
Promise.resolve的用法相当于new Promise,并且执行resolve操作:

Promise.resolve("cy")//等价于newPromise((resolve,reject)=>{resolve("cy")})

reject
reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态
Promise.reject的用法相当于new Promise,只是会调用reject

Promise.reject("cy")//等价于newPromise((resolve,reject)=>{rejecct("cy")})

all
Promise.all:
它的作用是将多个Promise包裹在一起形成一个新的Promise;
新的Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组
  • 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数
// 创建多个Promiseconst p1 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(11111)},1000);})const p2 =newPromise((resolve, reject)=>{setTimeout(()=>{reject(22222)},2000);})const p3 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(33333)},3000);})//  所有的Promise都变成fulfilled时, 再拿到结果// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
Promise.all([p2, p1, p3,"aaaa"]).then(res=>{
  console.log(res)}).catch(err=>{
  console.log("err:", err)//22222})

allSettled
在ES11(ES2020)中,添加了新的API Promise.allSettled:
该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
并且这个Promise的结果一定是fulfilled的:

allSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
这个对象中包含status状态,以及对应的value值;

// 创建多个Promiseconst p1 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(11111)},1000);})const p2 =newPromise((resolve, reject)=>{setTimeout(()=>{reject(22222)},2000);})const p3 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(33333)},3000);})// allSettled
Promise.allSettled([p1, p2, p3]).then(res=>{
  console.log(res)}).catch(err=>{
  console.log(err)})// [//   { status: 'fulfilled', value: 11111 },//   { status: 'rejected', reason: 22222 },//   { status: 'fulfilled', value: 33333 }// ]

race
race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果

// 创建多个Promiseconst p1 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(11111)},3000);})const p2 =newPromise((resolve, reject)=>{setTimeout(()=>{reject(22222)},500);})const p3 =newPromise((resolve, reject)=>{setTimeout(()=>{resolve(33333)},1000);})// race: 竞技/竞赛// 只要有一个Promise变成fulfilled状态, 那么就结束
Promise.race([p1, p2, p3]).then(res=>{
  console.log("res:", res)}).catch(err=>{
  console.log("err:", err)})// err: 22222

any:
any方法是ES12中新增的方法,和race方法是类似的:

any方法会等到一个fulfilled状态,才会决定新Promise的状态;
如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态
如果所有的Promise都是reject的,那么会报一个AggregateError的错误

注意:如果所有的Promise都是reject的,那么会报一个AggregateError的错误,可以通过err.error获取reject传入的参数

// 创建多个Promiseconst p1 =newPromise((resolve, reject)=>{setTimeout(()=>{// resolve(11111)reject(1111)},1000);})const p2 =newPromise((resolve, reject)=>{setTimeout(()=>{reject(22222)},500);})const p3 =newPromise((resolve, reject)=>{setTimeout(()=>{// resolve(33333)reject(3333)},3000);})// any方法
Promise.any([p1, p2, p3]).then(res=>{
    console.log("res:", res)}).catch(err=>{
    console.log("err:", err.errors)})

手写实现Promise

大致思路:
首先.因为promise是一个类。所以我们在手写实现promise时,也要新建一个类,promise在使用时,通常会传入一个回调函数,参数为resolve、reject,并且由于这个回调函数是立即执行的,所以我们可以在类的construct里面实现resolve、reject函数,在类的内部实现then方法。其他方法这里暂时不做实现。由于promise有三个状态,所以我们定义三个变量区分这三个状态,初始时为pending.另外由于resolve、reject传入的参数会在then和catch的回调函数里面作为他们的参数,因此我们需要用2个变量分别保存他们。由于会有多个then连接的情况还会有多次抛出错误的情况,我们使用2个数组存储这些回调。
constructor里面实现resolve、reject函数

constPROMISE_STATUS_PENDING='pending'constPROMISE_STATUS_FULFILLED='fulfilled'constPROMISE_STATUS_REJECTED='rejected'// 工具函数,捕获异常用functionexecFunctionWithCatchError(execFn, value, resolve, reject){try{const result =execFn(value)resolve(result)}catch(err){reject(err)}}constructor(executor){this.status =PROMISE_STATUS_PENDINGthis.value =undefinedthis.reason =undefinedthis.onFulfilledFns =[]this.onRejectedFns =[]constresolve=(value)=>{if(this.status ===PROMISE_STATUS_PENDING){// 添加微任务queueMicrotask(()=>{if(this.status !==PROMISE_STATUS_PENDING)returnthis.status =PROMISE_STATUS_FULFILLEDthis.value = value
          this.onFulfilledFns.forEach(fn=>{fn(this.value)})});}}constreject=(reason)=>{if(this.status ===PROMISE_STATUS_PENDING){// 添加微任务queueMicrotask(()=>{if(this.status !==PROMISE_STATUS_PENDING)returnthis.status =PROMISE_STATUS_REJECTEDthis.reason = reason
          this.onRejectedFns.forEach(fn=>{fn(this.reason)})})}}try{executor(resolve, reject)}catch(err){reject(err)}}

then方法:

then(onFulfilled, onRejected){returnnewHYPromise((resolve, reject)=>{// 1.如果在then调用的时候, 状态已经确定下来if(this.status ===PROMISE_STATUS_FULFILLED&& onFulfilled){// try {//   const value = onFulfilled(this.value)//   resolve(value)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onFulfilled,this.value, resolve, reject)}if(this.status ===PROMISE_STATUS_REJECTED&& onRejected){// try {//   const reason = onRejected(this.reason)//   resolve(reason)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onRejected,this.reason, resolve, reject)}// 2.将成功回调和失败的回调放到数组中if(this.status ===PROMISE_STATUS_PENDING){this.onFulfilledFns.push(()=>{// try {//   const value = onFulfilled(this.value)//   resolve(value)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onFulfilled,this.value, resolve, reject)})this.onRejectedFns.push(()=>{// try {//   const reason = onRejected(this.reason)//   resolve(reason)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onRejected,this.reason, resolve, reject)})}})}}

总代码包含测试代码:

constPROMISE_STATUS_PENDING='pending'constPROMISE_STATUS_FULFILLED='fulfilled'constPROMISE_STATUS_REJECTED='rejected'// 工具函数functionexecFunctionWithCatchError(execFn, value, resolve, reject){try{const result =execFn(value)resolve(result)}catch(err){reject(err)}}classHYPromise{constructor(executor){this.status =PROMISE_STATUS_PENDINGthis.value =undefinedthis.reason =undefinedthis.onFulfilledFns =[]this.onRejectedFns =[]constresolve=(value)=>{if(this.status ===PROMISE_STATUS_PENDING){// 添加微任务queueMicrotask(()=>{if(this.status !==PROMISE_STATUS_PENDING)returnthis.status =PROMISE_STATUS_FULFILLEDthis.value = value
          this.onFulfilledFns.forEach(fn=>{fn(this.value)})});}}constreject=(reason)=>{if(this.status ===PROMISE_STATUS_PENDING){// 添加微任务queueMicrotask(()=>{if(this.status !==PROMISE_STATUS_PENDING)returnthis.status =PROMISE_STATUS_REJECTEDthis.reason = reason
          this.onRejectedFns.forEach(fn=>{fn(this.reason)})})}}try{executor(resolve, reject)}catch(err){reject(err)}}then(onFulfilled, onRejected){returnnewHYPromise((resolve, reject)=>{// 1.如果在then调用的时候, 状态已经确定下来if(this.status ===PROMISE_STATUS_FULFILLED&& onFulfilled){// try {//   const value = onFulfilled(this.value)//   resolve(value)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onFulfilled,this.value, resolve, reject)}if(this.status ===PROMISE_STATUS_REJECTED&& onRejected){// try {//   const reason = onRejected(this.reason)//   resolve(reason)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onRejected,this.reason, resolve, reject)}// 2.将成功回调和失败的回调放到数组中if(this.status ===PROMISE_STATUS_PENDING){this.onFulfilledFns.push(()=>{// try {//   const value = onFulfilled(this.value)//   resolve(value)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onFulfilled,this.value, resolve, reject)})this.onRejectedFns.push(()=>{// try {//   const reason = onRejected(this.reason)//   resolve(reason)// } catch(err) {//   reject(err)// }execFunctionWithCatchError(onRejected,this.reason, resolve, reject)})}})}}const promise =newHYPromise((resolve, reject)=>{
  console.log("状态pending")// resolve(1111) // resolved/fulfilledreject(2222)// throw new Error("executor error message")})// 调用then方法多次调用
promise.then(res=>{
  console.log("res1:", res)return"aaaa"// throw new Error("err message")},err=>{
  console.log("err1:", err)return"bbbbb"// throw new Error("err message")}).then(res=>{
  console.log("res2:", res)},err=>{
  console.log("err2:", err)})

本文转载自: https://blog.csdn.net/weixin_50926010/article/details/124678230
版权归原作者 既白biu 所有, 如有侵权,请联系我们删除。

“【从用法到手写实现】一文带你深入理解面试之Promise”的评论:

还没有评论