你就卷吧,promise手写已经是面试常考题了!

经过大家的不懈努力,前端实习生的面试也是癫成了大家看不懂的存在。本人目前大三,在promise还没用明白的年纪,也是要会手写promise了。man~!what can i say.学吧孩子。以下是我对promise的一些拙见,希望能够帮助你我面试成功。

面试中promise的常考形式

  • 谈谈你对promise的看法

面对这一类的问题时,留给我们的回答空间很大。但通常面对这一类问题我们又不知从何处开口。这里提供一种思路给大家:先从promise是什么开始,再映射到其他相关问题上。所以就会牵扯出以下几个相关的问题出来。

diff 复制代码
- 为什么会出现
- 常用的API有哪些
- 手写promise
- promise有什么缺陷

这里一个个给大家好好谈谈。

什么是promise

在promise出现之前,我们在需要连续进行多个异步操作,并且每个操作都依赖于前一个操作完成后才能执行时,是通过不断的回调嵌套来达到目的。而这些代码往往难以阅读和维护,这种情况称之为回调地狱 。所以promise的出现避免了这种情况的发生,也就是说:promise解决了回调地狱的问题

有哪些API

  • Promise.resolve
  • Promise.reject
  • Promise.all
  • Promise.race
  • Promise.allSettled
  • Promise.any
  • promise.try
  • Promise.prototype.catch
  • Promise.prototype.finally
  • Promise.prototype.then

这么多API当然不用全都记住,记住几个常用的就行了,可以对这几个常用的进行深入的说明,如何用的以及如何打造的。如果全部方法都能记住当然是加分的。

手写Promise

要会手写,就得知道Promise有哪些功能,清晰的思路能够帮助我们更好的输出。

理解Promise

  • 下面这段话你也可以在适当的时候输出给面试官,如果你感觉面试官还挺愿意听你讲的时候时候。

Promise是一个构造函数,是异步编程的一种解决方案,通过Promise对象我们可以获取异步操作的消息。而Promise最主要的特性就是:链式调用状态转换错误处理

Promise有三种状态:panding(进行中)、fulfilled(已成功)、rejected(已失败)。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,就不会再变了,会一直保持这个结果。

链式调用指通过.then().catch()方法将多个Promise操作连接起来,使得每个Promise的解决成功(fulfilled)或失败(rejected)值可以被传递给下一个.then().catch()方法进行处理。

在Promise中,错误处理通常通过.catch()方法来实现,它允许你指定一个回调函数来处理Promise链中发生的任何错误,你可以在一个集中的地方处理所有可能出现的错误。

手写,实现一个Promise

这里我们实现一个简单的Promise,包括状态的转换,then()和catch()方法。

kotlin 复制代码
class myPromise {
    constructor(executor) {
        this.status = 'pending'
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallback = []   //then 的回调
        this.onRejectedCallback = []    //catch 的回调

        // 改变Promise状态为fulfilled,并调用所有成功的回调函数  
        const resolve = (value) => {
            if (this.status === 'pending') {
                this.status = 'fulfilled'
                this.value = value
                //then 的回调在这里触发
                this.onFulfilledCallback.forEach(callback => callback(value))
            }
        }

        // 改变Promise状态为rejected,并调用所有失败的回调函数  
        const reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected'
                this.reason = reason
                //catch 的回调在这里触发
                this.onRejectedCallback.forEach(callback => callback(reason))
            }
        }
        executor(resolve, reject)
    }

    // then方法用于注册成功和失败的回调函数 
    then(onFulfilled, onRejected) {
         // 处理默认情况,如果没有提供回调函数,则使用默认函数  
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}

        const newPromise =  new myPromise((resolve, reject) => {
            if(this.status === 'fulfilled') {
                setTimeout(() => {
                    try {
                        const result = onFulfilled(this.value)
                        resolve(result)
                    }
                    catch (error) {
                        reject(error)
                    }
                }, 0)
            }

            if(this.status === 'rejected') {
                setTimeout(() => { // 模拟异步,但是模拟不了微任务
                    try {
                        const result = onRejected(this.reason)
                        resolve(result)
                    }
                    catch (error) {
                        resolve(error)
                    }
                }, 0)
            }
    
            // 如果Promise处于pending状态,则将回调函数保存到队列中
            if (this.status === 'pending') {
                this.onFulfilledCallback.push(() => {
                    setTimeout(() => {
                        try {
                            const result = onFulfilled()
                            resolve(result)
                        } catch (error) {
                            reject(error)
                        }
                    },0)
                })
                this.onRejectedCallback.push(()=> {
                    setTimeout(() => {
                        try {
                            const result = onRejected()
                            resolve(result)
                        } catch (error) {
                            reject(error)
                        }
                    },0)
                })
            }
        })
        return newPromise
    }
}

Promise/A+规范

一个完整的Promise必须符合以下规范:(Promise/A+规范

  1. 状态:一个Promise必须处在以下三种状态之一:pending(等待态)、fulfilled(执行态)或rejected(拒绝态)。Pending状态可以转移到fulfilled或rejected状态,但一旦Promise进入fulfilled或rejected状态,就不可再变。
  2. :当Promise处于fulfilled状态时,它必须持有一个值,这个值被称为"value"。当Promise处于rejected状态时,它必须持有一个原因,这个原因被称为"reason"。
  3. then方法:Promise对象都有一个then方法,它用于注册两个回调函数,一个用于处理fulfilled状态,另一个用于处理rejected状态。then方法总是返回一个Promise,这使得可以链式调用Promise。
  4. 错误处理:Promise/A+规范提供了统一的错误处理机制。如果一个Promise被rejected,并且没有提供rejected状态的回调函数,那么错误会被传播到链中的下一个Promise。如果链中的最后一个Promise被rejected,并且没有提供rejected状态的回调函数,那么会调用全局的unhandledrejection事件处理器(如果存在)。
  5. 解析过程:规范引入了一种叫Promise解析过程的抽象过程,标记为[[Resolve]](promise, x)。这个过程用于将非标准的类Promise接口、对象和函数最终规范成一个标准的Promise。

注意,以下提到的API均写在上面的class中。

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

ini 复制代码
const p = Promise.all([p1, p2, p3]);

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

实现:

javascript 复制代码
static all(promises) {
    return new myPromise((resolve, reject) => {
      let count = 0
      let arr = []

      // 判断promises中的所有的promise状态是否都为fulfilled
      promises.forEach((promise, i) => {
        promise.then(
          (value) => {
            count++
            arr[i] = value
            if (count === promises.length) {
              resolve(arr)
            }
          },
          (reason) => {
            reject(reason)
          }

        )
      })

    })
  }

Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

实现:

javascript 复制代码
static any(promises) {
    return new myPromise((resolve, reject) => {
      let count = 0, arr = []

      promises.forEach((promise, i) => {
        promise.then(
          (value) => {
            resolve(value)
          },
          (reason) => {
            count++
            arr[i] = reason
            if (count === promises.length) {
              reject(new AggregateError(arr, 'All promises were rejected'))
            }
          }
        )
      })
    })
  }

Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

javascript 复制代码
finally(callback) {
    return this.then(
      (value) => {
        return Promise.resolve(callback()).then(() => value)
      },
      (reason) => {
        return Promise.resolve(callback()).then(() => reason)
      }
    )
  }

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

ini 复制代码
const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

javascript 复制代码
 static race(promises) {
        return new myPromise((resolve, reject) => {
            promises.forEach((promise) => {
                promise.then((value) => {
                    resolve(value)
                },
                (reason) => {
                    reject(reason)
                }
                )
            })
        })
    }

Promise.allSettled()

Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

javascript 复制代码
static allSettled(promises) {
    return new myPromise((resolve, reject) => {
      let arr = [], count = 0

      promises.forEach((promise, i) => {
        promise.then(
          (value) => {
            arr[i] = {status: 'fulfilled', value: value }
          },
          (reason) => {
            arr[i] = {status: 'rejected', reason: reason }
          }
        ).finally(() => {
          count++
          // 所有promise状态都变更了
          if (count === promises.length) {
            resolve(arr)
          }
          
        })
      })       
    })
  }

关于Promise,一定要理解原理,把常用的API记住是什么用法,这些才是面试中重要的。至于手写......理解之后多写几次应该就可以了,毕竟不多写几次就是很容易忘。至于手写Promise是不是常考题,大家见仁见智吧。

相关推荐
啧不应该啊13 分钟前
vue配置axios
前端·javascript·vue.js
__fuys__17 分钟前
【HTML样式】加载动画专题 每周更新
前端·javascript·html
Want59520 分钟前
HTML粉色烟花秀
前端·css·html
让开,我要吃人了25 分钟前
HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践
前端·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·鸿蒙开发
hn小菜鸡41 分钟前
LeetCode 面试经典150题 67.二进制求和
算法·leetcode·面试
一条晒干的咸魚43 分钟前
响应式CSS 媒体查询——WEB开发系列39
前端·css·html·css3·响应式设计·媒体查询
江凡心1 小时前
Qt 每日面试题 -2
开发语言·数据库·qt·面试
凌晨五点的星1 小时前
网络安全-webshell绕过,hash碰撞,webshell绕过原理
开发语言·前端·javascript
天心天地生1 小时前
【bugfix】-洽谈回填的图片消息无法显示
开发语言·前端·javascript
啧不应该啊1 小时前
element plus 按需导入vue
前端·javascript·vue.js