
1. Promise 实现原理与手写指南作为现代 JavaScript 异步编程的核心Promise 早已成为每个前端开发者必须掌握的技能点。但真正理解 Promise 的内部机制并能从零实现一个符合规范的 Promise 类才是区分会用和精通的关键分水岭。本文将带你深入 Promise 的实现细节手把手教你写出一个工业级可用的 MyPromise 类。2. Promise/A 规范解析2.1 核心状态机设计Promise 本质上是一个状态机包含三个基本状态pending初始状态既不是成功也不是失败fulfilled操作成功完成rejected操作失败状态转换是不可逆的一旦从 pending 变为 fulfilled 或 rejected就不能再改变状态。这是 Promise 可靠性的基石。class MyPromise { constructor(executor) { this.state pending; this.value undefined; this.reason undefined; this.onFulfilledCallbacks []; this.onRejectedCallbacks []; const resolve (value) { if (this.state pending) { this.state fulfilled; this.value value; this.onFulfilledCallbacks.forEach(fn fn()); } }; const reject (reason) { if (this.state pending) { this.state rejected; this.reason reason; this.onRejectedCallbacks.forEach(fn fn()); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } }2.2 then 方法的实现要点then 方法是 Promise 链式调用的核心需要处理以下情况当前 Promise 已完成直接执行回调当前 Promise 未完成将回调存入队列返回值穿透如果 then 没有提供回调值需要透传给下一个 Promisethen(onFulfilled, onRejected) { // 参数校验和默认值处理 onFulfilled typeof onFulfilled function ? onFulfilled : value value; onRejected typeof onRejected function ? onRejected : reason { throw reason }; 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); } }, 0); } else if (this.state rejected) { setTimeout(() { try { const x onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } else { this.onFulfilledCallbacks.push(() { setTimeout(() { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() { setTimeout(() { try { const x onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); } }); return promise2; }3. 完整实现与关键细节3.1 resolvePromise 的实现这是 Promise 实现中最复杂的部分需要处理各种返回值情况普通值直接 resolvePromise 对象等待其状态改变thenable 对象按 Promise 处理function resolvePromise(promise2, x, resolve, reject) { // 防止循环引用 if (promise2 x) { return reject(new TypeError(Chaining cycle detected for promise)); } // 防止重复调用 let called false; if (x ! null (typeof x object || typeof x function)) { try { const then x.then; if (typeof then function) { then.call( x, y { if (called) return; called true; resolvePromise(promise2, y, resolve, reject); }, r { if (called) return; called true; reject(r); } ); } else { resolve(x); } } catch (e) { if (called) return; called true; reject(e); } } else { resolve(x); } }3.2 其他必要方法实现一个完整的 Promise 实现还需要以下方法// catch 方法实际上是 then 的语法糖 catch(onRejected) { return this.then(null, onRejected); } // finally 方法无论成功失败都会执行 finally(callback) { return this.then( value MyPromise.resolve(callback()).then(() value), reason MyPromise.resolve(callback()).then(() { throw reason }) ); } // 静态方法 resolve static resolve(value) { if (value instanceof MyPromise) { return value; } return new MyPromise(resolve resolve(value)); } // 静态方法 reject static reject(reason) { return new MyPromise((_, reject) reject(reason)); } // 静态方法 all static 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 ); }); }); } // 静态方法 race static race(promises) { return new MyPromise((resolve, reject) { promises.forEach(promise { MyPromise.resolve(promise).then(resolve, reject); }); }); }4. 常见问题与调试技巧4.1 异步执行顺序问题Promise 的回调总是异步执行的即使 Promise 已经 resolved。这是规范要求的确保执行顺序的一致性。console.log(1); new MyPromise(resolve { console.log(2); resolve(3); }).then(console.log); console.log(4); // 正确输出顺序1, 2, 4, 34.2 错误捕获陷阱Promise 内部的错误如果不被捕获会导致静默失败。最佳实践是始终添加 catch 处理。// 不好的写法 new MyPromise((_, reject) reject(error)); // 好的写法 new MyPromise((_, reject) reject(error)) .catch(e console.error(Caught:, e));4.3 内存泄漏排查未处理的 Promise 拒绝可能导致内存泄漏。在 Node.js 中可以这样监听process.on(unhandledRejection, (reason, promise) { console.error(Unhandled Rejection at:, promise, reason:, reason); });5. 性能优化与实践建议5.1 微任务队列优化现代浏览器和 Node.js 使用微任务队列实现 Promise。手写实现时使用 setTimeout 模拟但真实环境中性能更好。5.2 链式调用深度限制过长的 Promise 链可能导致栈溢出。解决方案是定期使用 setTimeout 打破调用栈function runNextTick(fn) { return new Promise(resolve { setTimeout(() resolve(fn()), 0); }); } async function longRunningTask() { let result await initialTask(); for (let i 0; i 1000; i) { result await runNextTick(() nextStep(result)); } return result; }5.3 Promise 池控制当处理大量并发 Promise 时需要控制并发数量class PromisePool { constructor(maxConcurrent) { this.maxConcurrent maxConcurrent; this.queue []; this.running 0; } add(task) { return new Promise((resolve, reject) { this.queue.push({ task, resolve, reject }); this.run(); }); } run() { while (this.running this.maxConcurrent this.queue.length) { const { task, resolve, reject } this.queue.shift(); this.running; task() .then(resolve) .catch(reject) .finally(() { this.running--; this.run(); }); } } }6. 测试与验证6.1 使用 Promises/A 测试套件官方测试套件可以验证你的实现是否符合规范npm install promises-aplus-tests -D创建适配器文件const MyPromise require(./my-promise); module.exports { resolved: MyPromise.resolve, rejected: MyPromise.reject, deferred() { const result {}; result.promise new MyPromise((resolve, reject) { result.resolve resolve; result.reject reject; }); return result; } };然后运行测试promises-aplus-tests adapter.js6.2 边界条件测试案例确保测试以下边界条件在 executor 中同步 resolve在 executor 中异步 rejectthen 方法多次调用then 方法链式调用返回 Promise循环引用检测// 循环引用测试 const p1 new MyPromise(resolve { const p2 new MyPromise(resolve resolve(p1)); resolve(p2); }); p1.catch(e console.error(Caught cycle:, e.message));7. 进阶实现技巧7.1 取消功能扩展原生 Promise 不支持取消但我们可以扩展实现class CancelablePromise extends MyPromise { constructor(executor) { let _reject; super((resolve, reject) { _reject reject; executor(resolve, reject); }); this.cancel () { _reject(new Error(Promise canceled)); }; } } // 使用示例 const p new CancelablePromise(resolve { setTimeout(() resolve(done), 1000); }); p.catch(e console.log(e.message)); // Promise canceled p.cancel();7.2 进度通知扩展某些场景需要进度通知class ProgressPromise extends MyPromise { constructor(executor) { const progressCallbacks []; super((resolve, reject) { executor( resolve, reject, progress { progressCallbacks.forEach(cb cb(progress)); } ); }); this.progress cb { progressCallbacks.push(cb); return this; }; } } // 使用示例 new ProgressPromise((resolve, reject, progress) { let percent 0; const interval setInterval(() { percent 10; progress(percent); if (percent 100) { clearInterval(interval); resolve(); } }, 200); }) .progress(p console.log(${p}% complete)) .then(() console.log(Done));7.3 超时控制实现为 Promise 添加超时功能MyPromise.prototype.timeout function(ms, message Promise timeout) { return MyPromise.race([ this, new MyPromise((_, reject) setTimeout(() reject(new Error(message)), ms) ) ]); }; // 使用示例 new MyPromise(resolve setTimeout(resolve, 2000, slow) ) .timeout(1000) .catch(e console.log(e.message)); // Promise timeout8. 与 async/await 的配合8.1 在 async 函数中使用手写的 MyPromise 完全可以与 async/await 语法配合使用async function example() { try { const result await new MyPromise(resolve setTimeout(resolve, 100, done) ); console.log(result); // done } catch (e) { console.error(e); } }8.2 实现 async 辅助函数基于 MyPromise 实现常用的 async 辅助函数function delay(ms, value) { return new MyPromise(resolve setTimeout(resolve, ms, value) ); } function timeout(promise, ms, message Timeout) { return MyPromise.race([ promise, delay(ms).then(() { throw new Error(message) }) ]); } async function retry(fn, retries 3, delayMs 1000) { try { return await fn(); } catch (e) { if (retries 0) throw e; await delay(delayMs); return retry(fn, retries - 1, delayMs); } }9. 浏览器与 Node.js 环境差异9.1 微任务队列差异浏览器使用 MutationObserver 或微任务队列Node.js使用 process.nextTick在手写实现中我们使用 setTimeout 模拟但在实际环境中// 环境敏感的异步调度 const nextTick (() { if (typeof process object process.nextTick) { return process.nextTick; } if (typeof MutationObserver ! undefined) { let counter 1; const textNode document.createTextNode(String(counter)); const observer new MutationObserver(() { callback(); counter (counter 1) % 2; textNode.data String(counter); }); observer.observe(textNode, { characterData: true }); return callback { textNode.data String((counter (counter 1) % 2)); }; } return callback setTimeout(callback, 0); })();9.2 未处理拒绝警告Node.js 有专门的 unhandledRejection 事件而浏览器环境需要 polyfillif (typeof window ! undefined) { window.addEventListener(unhandledrejection, event { console.warn(Unhandled rejection:, event.reason); event.preventDefault(); }); }10. 实际应用场景10.1 请求队列管理实现一个带并发控制的请求队列class RequestQueue { constructor(concurrency 4) { this.queue []; this.active 0; this.concurrency concurrency; } add(requestFn) { return new MyPromise((resolve, reject) { this.queue.push({ requestFn, resolve, reject }); this.next(); }); } next() { if (this.active this.concurrency || !this.queue.length) return; this.active; const { requestFn, resolve, reject } this.queue.shift(); MyPromise.resolve(requestFn()) .then(resolve) .catch(reject) .finally(() { this.active--; this.next(); }); } } // 使用示例 const queue new RequestQueue(2); for (let i 0; i 10; i) { queue.add(() fetch(/api/data/${i})) .then(response console.log(Got:, i)); }10.2 数据库事务封装用 Promise 封装数据库事务class Database { beginTransaction() { return new MyPromise((resolve, reject) { this.connection.beginTransaction(err { if (err) return reject(err); resolve(); }); }); } query(sql, params) { return new MyPromise((resolve, reject) { this.connection.query(sql, params, (err, results) { if (err) return reject(err); resolve(results); }); }); } commit() { return new MyPromise((resolve, reject) { this.connection.commit(err { if (err) return this.rollback().finally(() reject(err)); resolve(); }); }); } rollback() { return new MyPromise(resolve { this.connection.rollback(() resolve()); }); } async executeInTransaction(callback) { await this.beginTransaction(); try { const result await callback(); await this.commit(); return result; } catch (e) { await this.rollback(); throw e; } } }11. 性能对比与优化11.1 原生 Promise vs 手写实现在大多数现代 JavaScript 引擎中原生 Promise 经过高度优化V8 使用微任务队列和内部优化手写实现通常慢 2-5 倍11.2 内存占用分析Promise 链会保持引用直到完成长链可能导致内存压力。解决方案定期打断长链避免在 Promise 中保存大对象// 不好的做法保持大对象引用 function processLargeData(data) { return validate(data) .then(() transform(data)) // 保持 data 引用 .then(result save(result)); } // 好的做法尽早释放 function processLargeData(data) { return validate(data) .then(() { const result transform(data); data null; // 显式释放 return result; }) .then(save); }12. 调试与错误追踪12.1 增强错误堆栈Promise 链可能导致错误堆栈不完整可以增强class TraceablePromise extends MyPromise { constructor(executor) { const trace new Error().stack.split(\n).slice(2).join(\n); super((resolve, reject) { executor( value resolve(value), reason { if (reason instanceof Error !reason.stack.includes(trace)) { reason.stack \n--- Promise created at ---\n trace; } reject(reason); } ); }); } }12.2 异步上下文跟踪使用 AsyncLocalStorageNode.js跟踪异步调用链const { AsyncLocalStorage } require(async_hooks); const asyncLocalStorage new AsyncLocalStorage(); class ContextAwarePromise extends MyPromise { constructor(executor) { const context asyncLocalStorage.getStore(); super((resolve, reject) { asyncLocalStorage.run(context, () { executor(resolve, reject); }); }); } }13. 与 Generator 的配合13.1 自动执行 Generator实现类似 co 库的功能function runGenerator(gen) { return new MyPromise((resolve, reject) { const g gen(); function step(value) { try { const { done, value: result } g.next(value); if (done) return resolve(result); MyPromise.resolve(result).then( val step(val), err g.throw(err) ); } catch (e) { reject(e); } } step(); }); } // 使用示例 runGenerator(function* () { const a yield new MyPromise(resolve setTimeout(resolve, 100, 1)); const b yield new MyPromise(resolve setTimeout(resolve, 100, 2)); return a b; }).then(console.log); // 313.2 实现 async/await 的 polyfill基于 Generator 和 Promise 实现 async/awaitfunction asyncToGenerator(fn) { return function(...args) { return runGenerator(fn.bind(this, ...args)); }; } // 使用示例 const asyncFunc asyncToGenerator(function*(a, b) { const x yield new MyPromise(resolve setTimeout(resolve, 100, a)); const y yield new MyPromise(resolve setTimeout(resolve, 100, b)); return x y; }); asyncFunc(3, 4).then(console.log); // 714. 测试驱动开发实践14.1 单元测试策略为 Promise 实现编写全面的单元测试describe(MyPromise, () { it(should resolve with value, done { new MyPromise(resolve resolve(42)) .then(value { assert.equal(value, 42); done(); }); }); it(should reject with reason, done { new MyPromise((_, reject) reject(error)) .catch(reason { assert.equal(reason, error); done(); }); }); it(should chain promises, done { new MyPromise(resolve resolve(1)) .then(x x 1) .then(x x * 2) .then(value { assert.equal(value, 4); done(); }); }); it(should handle async operations, done { new MyPromise(resolve setTimeout(() resolve(async), 50) ).then(value { assert.equal(value, async); done(); }); }); });14.2 压力测试方案测试 Promise 实现的稳定性和性能function stressTest(count 10000) { return new MyPromise(resolve { let completed 0; const start Date.now(); for (let i 0; i count; i) { new MyPromise(r r(i)) .then(x x * 2) .then(x { completed; if (completed count) { resolve(Date.now() - start); } }); } }); } stressTest().then(duration { console.log(Completed in ${duration}ms); });15. 与其他异步模式的对比15.1 回调函数 vs Promise回调地狱问题// 回调地狱 getData(a { processA(a, b { processB(b, c { processC(c, d { console.log(d); }); }); }); }); // Promise 解决方案 getData() .then(processA) .then(processB) .then(processC) .then(console.log);15.2 EventEmitter vs Promise事件驱动与 Promise 的对比EventEmitter多个监听者多次触发Promise单个结果一次性解决// 混合使用示例 function eventToPromise(emitter, event) { return new MyPromise(resolve { emitter.once(event, resolve); }); } // 使用示例 const fs require(fs); const stream fs.createReadStream(file.txt); eventToPromise(stream, open) .then(() console.log(File opened)) .catch(console.error);16. 浏览器兼容性处理16.1 旧版浏览器支持在不支持 Promise 的环境中使用 polyfillif (typeof Promise undefined) { window.Promise MyPromise; }16.2 与 jQuery Deferred 互操作jQuery Deferred 对象可以转换为 Promisefunction jQueryToPromise(deferred) { return new MyPromise((resolve, reject) { deferred.then(resolve, reject); }); } // 使用示例 const jqDeferred $.ajax(/api/data); jQueryToPromise(jqDeferred) .then(data console.log(data));17. 安全注意事项17.1 防止 Promise 劫持确保 then 方法不会被恶意修改class SecurePromise extends MyPromise { then(onFulfilled, onRejected) { const originalThen super.then; return originalThen.call( this, typeof onFulfilled function ? onFulfilled : null, typeof onRejected function ? onRejected : null ); } }17.2 输入验证强化对 executor 函数进行严格验证class ValidatedPromise extends MyPromise { constructor(executor) { if (typeof executor ! function) { throw new TypeError(Promise executor must be a function); } let called false; super((resolve, reject) { try { executor( value { if (called) return; called true; resolve(value); }, reason { if (called) return; called true; reject(reason); } ); } catch (e) { if (!called) { called true; reject(e); } } }); } }18. 性能监控与调优18.1 Promise 执行时间统计监控 Promise 的执行时间function timedPromise(promise, name unknown) { const start performance.now(); return promise.then( value { console.log(Promise ${name} resolved in ${performance.now() - start}ms); return value; }, reason { console.error(Promise ${name} rejected after ${performance.now() - start}ms); throw reason; } ); } // 使用示例 timedPromise( new MyPromise(resolve setTimeout(resolve, 100)), delayed );18.2 内存泄漏检测跟踪 Promise 生命周期const activePromises new Set(); class TrackedPromise extends MyPromise { constructor(executor) { super((resolve, reject) { activePromises.add(this); executor( value { activePromises.delete(this); resolve(value); }, reason { activePromises.delete(this); reject(reason); } ); }); } } // 检查泄漏 setInterval(() { console.log(Active promises: ${activePromises.size}); }, 5000);19. 与 TypeScript 的集成19.1 类型定义为 MyPromise 添加 TypeScript 类型interface ThenableT { thenTResult1 T, TResult2 never( onFulfilled?: ((value: T) TResult1 | ThenableTResult1) | null, onRejected?: ((reason: any) TResult2 | ThenableTResult2) | null ): ThenableTResult1 | TResult2; } class MyPromiseT implements ThenableT { constructor( executor: ( resolve: (value: T | ThenableT) void, reject: (reason?: any) void ) void ) { // 实现... } thenTResult1 T, TResult2 never( onFulfilled?: ((value: T) TResult1 | ThenableTResult1) | null, onRejected?: ((reason: any) TResult2 | ThenableTResult2) | null ): MyPromiseTResult1 | TResult2 { // 实现... } catchTResult never( onRejected?: ((reason: any) TResult | ThenableTResult) | null ): MyPromiseT | TResult { return this.then(null, onRejected); } }19.2 泛型应用利用 TypeScript 泛型增强类型安全function fetchUser(id: number): MyPromise{ name: string } { return new MyPromise(resolve { setTimeout(() resolve({ name: User ${id} }), 100); }); } fetchUser(1).then(user { console.log(user.name.toUpperCase()); // 类型安全 });20. 总结与最佳实践20.1 核心要点回顾Promise 是一个状态机有三种状态pending、fulfilled、rejectedthen 方法必须返回新的 Promise 以实现链式调用需要处理各种返回值情况包括 thenable 对象错误必须被捕获否则会导致静默失败20.2 推荐实践总是返回或终止 Promise 链使用 catch 处理错误避免在 Promise 中抛出同步错误对长时间运行的 Promise 添加超时控制在适当的时候使用 Promise.all 并行处理20.3 进一步学习方向阅读 Promises/A 规范原文研究主流 Promise 库的源码如 bluebird学习 async/await 的实现原理探索 RxJS 等响应式编程库通过手写实现 Promise不仅能深入理解异步编程的本质还能掌握 JavaScript 的核心工作机制。这种底层知识对于解决复杂的异步问题和性能优化至关重要。