你就卷吧,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是不是常考题,大家见仁见智吧。

相关推荐
腾讯TNTWeb前端团队29 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom4 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom5 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom5 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom5 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github