『JS手写』手写 Promise 跨年特辑:从原理到实现全解析
2025.09.19 12:47浏览量:2简介:本文深入解析如何手写实现符合Promise/A+规范的Promise类,包含状态管理、链式调用、异步调度等核心机制,提供可运行的完整代码和跨浏览器兼容方案。
『JS手写』手写 Promise 跨年特辑:从原理到实现全解析
一、为什么需要手写Promise?
在ES6正式将Promise纳入语言标准前,前端异步编程长期处于”回调地狱”的混乱状态。即使现在,理解Promise内部机制仍是深入掌握JavaScript异步编程的关键。手写Promise不仅能加深对Promise/A+规范的理解,更能培养解决复杂异步问题的能力,这在面试和源码级开发中具有重要价值。
1.1 历史背景
2015年ES6发布前,前端生态存在多种Promise实现:
- jQuery的Deferred对象
- Q.js库
- Bluebird等高性能实现
这些实现虽功能相似,但存在行为差异。Promise/A+规范的出现统一了社区标准。
1.2 核心价值
手写实现可带来:
- 深入理解thenable对象机制
- 掌握微任务调度原理
- 理解链式调用的实现技巧
- 培养规范意识(符合Promise/A+测试套件)
二、Promise核心机制解析
2.1 状态机模型
Promise必须实现三种状态转换:
const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';class MyPromise {constructor(executor) {this.state = PENDING;this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];}}
状态转换规则:
- 只能从pending转为fulfilled/rejected
- 一旦转换不可逆
- 转换后需执行对应回调队列
2.2 异步调度策略
实现异步的关键在于微任务调度:
// 兼容方案:优先使用微任务const resolvePromise = (promise, x, resolve, reject) => {// 使用MutationObserver或setTimeout模拟微任务if (typeof Promise !== 'undefined' && Promise.resolve) {Promise.resolve().then(() => executeCallback(x, resolve, reject));} else {setTimeout(() => executeCallback(x, resolve, reject), 0);}};
2.3 then方法实现
then方法是Promise的核心,需处理:
- 值穿透(连续then调用)
- 异步回调执行
链式调用支持
then(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(() => handleCallback(onFulfilled, this.value, resolve, reject), 0);} else if (this.state === REJECTED) {setTimeout(() => handleCallback(onRejected, this.reason, resolve, reject), 0);} else {this.onFulfilledCallbacks.push(value =>handleCallback(onFulfilled, value, resolve, reject));this.onRejectedCallbacks.push(reason =>handleCallback(onRejected, reason, resolve, reject));}});return promise2;}
三、完整实现与规范兼容
3.1 基础类实现
class MyPromise {constructor(executor) {this.state = PENDING;this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];const resolve = value => {if (value instanceof MyPromise) {return value.then(resolve, reject);}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);}}// ...then方法实现(同上)}
3.2 静态方法实现
// 类方法实现MyPromise.resolve = function(value) {return new MyPromise(resolve => resolve(value));};MyPromise.reject = function(reason) {return new MyPromise((_, reject) => reject(reason));};MyPromise.all = function(promises) {return new MyPromise((resolve, reject) => {const results = [];let count = 0;promises.forEach((promise, index) => {MyPromise.resolve(promise).then(value => {results[index] = value;if (++count === promises.length) resolve(results);},reject);});});};MyPromise.race = function(promises) {return new MyPromise((resolve, reject) => {promises.forEach(promise => {MyPromise.resolve(promise).then(resolve, reject);});});};
3.3 跨浏览器兼容方案
微任务模拟:
const asyncFlush = (() => {if (typeof Promise !== 'undefined') {return () => Promise.resolve().then(flush);} else if (typeof MutationObserver !== 'undefined') {let queue = [];const node = document.createTextNode('');new MutationObserver(() => {while (queue.length) queue.shift()();}).observe(node, { characterData: true });return callback => {queue.push(callback);node.data = Math.random();};} else {return callback => setTimeout(callback, 0);}})();
Object.prototype污染防护:
```javascript
const hasOwnProperty = Object.prototype.hasOwnProperty;
function isObject(obj) {
return obj !== null && typeof obj === ‘object’;
}
function getThen(x) {
const then = isObject(x) && hasOwnProperty.call(x, ‘then’) ? x.then : null;
if (typeof then === ‘function’) return then;
return null;
}
## 四、进阶实现技巧### 4.1 链式调用优化```javascriptthen(onFulfilled, onRejected) {const promise2 = new MyPromise((resolve, reject) => {const handleFulfilled = value => {try {if (typeof onFulfilled !== 'function') {resolve(value);} else {const x = onFulfilled(value);resolvePromise(promise2, x, resolve, reject);}} catch (e) {reject(e);}};// 类似实现handleRejected...});return promise2;}
4.2 性能优化策略
- 回调合并:使用单个定时器处理所有回调
- 错误边界:try-catch包裹所有回调执行
- 值缓存:避免重复计算thenable值
五、测试验证方案
5.1 基础测试用例
// 状态转换测试const promise = new MyPromise((resolve) => {setTimeout(() => resolve(1), 100);});promise.then(value => {console.log(value); // 应输出1});// 链式调用测试MyPromise.resolve(1).then(value => value + 1).then(value => value * 2).then(value => {console.log(value); // 应输出4});
5.2 规范兼容测试
使用promises-aplus-tests进行验证:
- 安装测试工具:
npm install promises-aplus-tests -g - 创建适配器:
const adapter = {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.js
六、实际应用建议
- 学习阶段:先理解核心逻辑,再逐步完善边缘情况
- 生产环境:优先使用原生Promise或成熟库(如Bluebird)
调试技巧:
- 使用
Promise.prototype.finally追踪流程 - 通过
console.log标记状态变化 - 利用开发者工具的Async堆栈追踪
- 使用
性能优化:
- 避免在then回调中创建新Promise
- 合理使用Promise.all处理并行请求
- 对可取消操作实现AbortController
七、总结与展望
手写Promise实现是理解JavaScript异步编程的终极试炼。通过实现过程,开发者不仅能掌握状态管理、异步调度等核心概念,更能培养规范意识和工程化思维。随着ES2023引入Promise.try等新特性,Promise生态仍在持续演进,但底层原理始终是理解新特性的基石。
建议读者在完成基础实现后,尝试:
- 实现Promise.allSettled
- 添加取消功能
- 研究async/await的编译原理
- 探索Generator函数与Promise的关系
通过持续实践和思考,开发者将构建起完整的异步编程知识体系,为处理复杂前端架构打下坚实基础。

发表评论
登录后可评论,请前往 登录 或 注册