第64篇:前端手写Promise完整源码(终极完整版·面试满分手撕)
前言之前我们学过 Promise 的用法但只会用不会手写面试不及格。Promise 是前端异步的根基能完整手写 Promise 源码代表你的JS基础远超90%求职者。本篇带你手写完整版 Promise兼容原生所有特性三种状态、resolve/reject、异步处理、then链式调用、catch、finally、all/race/allSettled。一、Promise核心原理必背三种状态pending(等待)、fulfilled(成功)、rejected(失败)状态一旦改变不可逆then 方法链式调用支持异步任务排队执行值穿透、异常冒泡二、手写完整版Promise可直接面试默写class MyPromise { // 初始状态 static PENDING pending static FULFILLED fulfilled static REJECTED rejected constructor(executor) { // 状态 this.status MyPromise.PENDING // 成功值 this.result null // 失败原因 this.reason null // 异步回调队列解决异步定时器问题 this.resolveCallbacks [] this.rejectCallbacks [] // 成功函数 const resolve (value) { if (this.status MyPromise.PENDING) { this.status MyPromise.FULFILLED this.result value // 执行所有成功回调 this.resolveCallbacks.forEach(fn fn()) } } // 失败函数 const reject (reason) { if (this.status MyPromise.PENDING) { this.status MyPromise.REJECTED this.reason reason // 执行所有失败回调 this.rejectCallbacks.forEach(fn fn()) } } // 捕获执行器异常 try { executor(resolve, reject) } catch (err) { reject(err) } } // 链式then then(onFulfilled, onRejected) { // 值穿透处理 onFulfilled typeof onFulfilled function ? onFulfilled : val val onRejected typeof onRejected function ? onRejected : err { throw err } // 返回新Promise实现链式调用 return new MyPromise((resolve, reject) { // 成功状态 if (this.status MyPromise.FULFILLED) { setTimeout(() { try { let res onFulfilled(this.result) resolve(res) } catch (err) { reject(err) } }) } // 失败状态 if (this.status MyPromise.REJECTED) { setTimeout(() { try { let res onRejected(this.reason) resolve(res) } catch (err) { reject(err) } }) } // 异步等待状态存入队列 if (this.status MyPromise.PENDING) { this.resolveCallbacks.push(() { setTimeout(() { try { let res onFulfilled(this.result) resolve(res) } catch (err) { reject(err) } }) }) this.rejectCallbacks.push(() { setTimeout(() { try { let res onRejected(this.reason) resolve(res) } catch (err) { reject(err) } }) }) } }) } // 错误捕获 catch(onRejected) { return this.then(null, onRejected) } // 最终执行 finally(fn) { return this.then( val { fn() return val }, err { fn() throw err } ) } // 静态all static all(list) { return new MyPromise((resolve, reject) { let res [] let count 0 for (let i 0; i list.length; i) { list[i].then(val { res[i] val count if (count list.length) resolve(res) }).catch(err reject(err)) } }) } // 静态race static race(list) { return new MyPromise((resolve, reject) { list.forEach(item { item.then(resolve).catch(reject) }) }) } // 全部完成不管成功失败 static allSettled(list) { return new MyPromise((resolve) { let res [] let count 0 list.forEach((item, idx) { item.then(val { res[idx] { status: fulfilled, value: val } }).catch(err { res[idx] { status: rejected, reason: err } }).finally(() { count if (count list.length) resolve(res) }) }) }) } }三、面试满分问答1、Promise 为什么可以链式调用因为 then/catch/finally 方法始终返回一个全新的Promise实例所以可以无限链式。2、Promise 状态为什么不可逆源码中只有 pending 状态允许变更一旦切换为成功/失败后续不再执行状态修改保证异步结果唯一性。3、值穿透是什么当 then 不传参数时结果会自动穿透到下一个 then源码中做了默认函数兜底处理。本篇结语能手写完整 Promise你的 JS 异步水平直接超越中级前端面试手撕题直接满分通关。