前言
之前我们学过 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 异步水平直接超越中级前端,面试手撕题直接满分通关。