『JS手写』手写 Promise 跨年特辑:从原理到实现全解析
2025.09.19 12:47浏览量:1简介:本文深入解析如何手写实现符合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 链式调用优化
```javascript
then(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的关系
通过持续实践和思考,开发者将构建起完整的异步编程知识体系,为处理复杂前端架构打下坚实基础。
发表评论
登录后可评论,请前往 登录 或 注册