手写Promise核心实现与异步编程原理

📅 2026/8/12 11:41:37
手写Promise核心实现与异步编程原理
1. 手写Promise的核心价值与实现意义第一次接触Promise这个概念是在2015年ES6标准发布时当时前端开发中回调地狱(callback hell)问题已经严重到令人发指的地步。我记得最夸张的一个项目里有个函数嵌套了8层回调代码缩进都快跑到屏幕右边去了。Promise的出现就像黑暗中的一束光而理解它的最好方式就是亲手实现一个。手写Promise不是面试造火箭的噱头它能让你彻底吃透Promise/A规范的核心机制理解异步编程的底层运作原理掌握更优雅的异步代码组织方式为理解更高级的async/await打下基础2. Promise核心机制解析2.1 状态机的设计哲学Promise本质上是一个状态机包含三个不可逆的状态pending初始状态既不是成功也不是失败fulfilled操作成功完成rejected操作失败这种设计保证了状态一旦改变就不会再变不可逆性无论成功或失败都会有一个确定的结果符合现实世界中承诺的语义class MyPromise { constructor(executor) { this.state pending; this.value undefined; this.reason undefined; // 后续会添加回调队列 } }2.2 执行器函数的双向通道executor是Promise构造函数接收的函数参数它有两个关键特性立即执行同步执行提供resolve/reject两个回调函数const promise new MyPromise((resolve, reject) { // 这里是同步执行的 if (success) { resolve(value); } else { reject(reason); } });关键细节如果在executor中抛出异常应该直接reject这个promise。这是很多人手写时容易忽略的边界情况。3. 实现基础状态管理3.1 状态转换的实现我们需要在MyPromise类中添加状态转换方法class MyPromise { // ...其他代码 _resolve(value) { if (this.state ! pending) return; this.state fulfilled; this.value value; // 后续会处理回调队列 } _reject(reason) { if (this.state ! pending) return; this.state rejected; this.reason reason; // 后续会处理回调队列 } }3.2 执行器的错误处理完善构造函数中的错误处理constructor(executor) { // ...初始化代码 try { executor( value this._resolve(value), reason this._reject(reason) ); } catch (err) { this._reject(err); } }4. 实现then方法4.1 then方法的基本结构then方法是Promise的核心它需要接收onFulfilled和onRejected两个回调返回一个新的Promise链式调用基础根据当前状态决定立即执行还是暂存回调then(onFulfilled, onRejected) { const promise2 new MyPromise((resolve, reject) { if (this.state fulfilled) { // 异步执行 setTimeout(() { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (this.state rejected) { // 类似处理rejected状态 } else { // pending状态时存储回调 } }); return promise2; }4.2 回调队列的实现处理pending状态时的回调存储constructor(executor) { // ...其他初始化 this.onFulfilledCallbacks []; this.onRejectedCallbacks []; } // 在_resolve中添加 this.onFulfilledCallbacks.forEach(fn fn()); // _reject中类似4.3 resolvePromise的实现这是Promise/A规范中最复杂的部分处理各种返回值情况function resolvePromise(promise2, x, resolve, reject) { // 防止循环引用 if (promise2 x) { return reject(new TypeError(Chaining cycle detected)); } // 如果x是Promise实例 if (x instanceof MyPromise) { x.then(resolve, reject); } // 处理thenable对象 else if (typeof x object || typeof x function) { if (x null) { return resolve(x); } let then; try { then x.then; } catch (e) { return reject(e); } if (typeof then function) { // 处理thenable } else { resolve(x); } } else { resolve(x); } }5. 完整实现与边界处理5.1 默认回调的处理根据Promise/A规范如果onFulfilled不是函数需要提供默认实现如果onRejected不是函数需要提供默认实现then(onFulfilled, onRejected) { onFulfilled typeof onFulfilled function ? onFulfilled : value value; onRejected typeof onRejected function ? onRejected : reason { throw reason; }; // ...其余代码 }5.2 异步执行的保证Promise的回调必须是异步执行的即使当前状态已经确定if (this.state fulfilled) { setTimeout(() { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }6. 实现其他静态方法6.1 Promise.resolvestatic resolve(value) { if (value instanceof MyPromise) { return value; } return new MyPromise(resolve { resolve(value); }); }6.2 Promise.rejectstatic reject(reason) { return new MyPromise((_, reject) { reject(reason); }); }6.3 Promise.allstatic all(promises) { return new MyPromise((resolve, reject) { const results []; let count 0; promises.forEach((promise, index) { MyPromise.resolve(promise).then( value { results[index] value; count; if (count promises.length) { resolve(results); } }, reject ); }); }); }7. 常见问题与调试技巧7.1 为什么then方法要返回新Promise这是实现链式调用的关键。每次调用then都创建一个新Promise使得我们可以无限衔接异步操作promise .then(handle1) .then(handle2) .then(handle3);7.2 如何处理thenable对象thenable是指有then方法的对象这是为了兼容其他Promise实现const thenable { then: function(resolve, reject) { resolve(42); } }; MyPromise.resolve(thenable).then(console.log); // 427.3 调试Promise的技巧在_resolve和_reject方法中添加console.log使用try-catch包裹所有回调执行验证微任务时序是否正确可以用queueMicrotask替代setTimeout8. 性能优化与实践建议8.1 避免不必要的Promise创建// 不推荐 function getData() { return new Promise(resolve { resolve(fetchData()); }); } // 推荐 function getData() { return Promise.resolve(fetchData()); }8.2 错误处理的最佳实践// 不好的方式 promise.then( handleSuccess, handleError ).then(...); // 这里的then仍然会执行 // 推荐方式 promise .then(handleSuccess) .catch(handleError); // 捕获链中任何错误8.3 取消功能的实现思路虽然Promise没有内置取消功能但可以通过包装实现function cancellablePromise(promise) { let isCancelled false; const wrappedPromise new Promise((resolve, reject) { promise.then( value !isCancelled resolve(value), error !isCancelled reject(error) ); }); return { promise: wrappedPromise, cancel: () { isCancelled true; } }; }9. Promise与事件循环的深度关联9.1 微任务队列的执行时机Promise回调属于微任务会在当前宏任务结束后立即执行console.log(script start); setTimeout(() { console.log(setTimeout); }, 0); Promise.resolve().then(() { console.log(promise1); }).then(() { console.log(promise2); }); console.log(script end); // 输出顺序 // script start // script end // promise1 // promise2 // setTimeout9.2 手写Promise中的异步调度在我们的实现中使用setTimeout模拟微任务实际上现代JavaScript引擎有更高效的微任务API// 更精确的微任务调度 const asyncFn typeof queueMicrotask function ? queueMicrotask : callback Promise.resolve().then(callback);10. 从Promise到async/await10.1 async函数的本质async函数其实就是返回Promise的语法糖async function foo() { return 1; } // 等价于 function foo() { return Promise.resolve(1); }10.2 await的工作机制await会暂停async函数执行直到Promise解决async function bar() { const result await somePromise; console.log(result); } // 类似 function bar() { return somePromise.then(result { console.log(result); }); }10.3 错误处理的演变从Promise的.catch到try-catch// Promise风格 fetchData() .then(handleData) .catch(handleError); // async/await风格 try { const data await fetchData(); handleData(data); } catch (err) { handleError(err); }手写Promise的过程让我深刻理解了JavaScript异步编程的演进历程。从最初的回调地狱到Promise的链式调用再到async/await的同步写法每一层抽象都让异步代码更易于编写和维护。建议每个前端开发者都应该至少完整实现一次Promise这会让你在调试复杂异步流程时事半功倍。