从零实现Promise:手写一个符合规范的Promise类库
2025.09.19 12:47浏览量:0简介:本文通过手写Promise实现,深入解析Promise核心机制,涵盖状态管理、链式调用、异步调度及错误处理,帮助开发者掌握异步编程的核心原理。
从零实现Promise:手写一个符合规范的Promise类库
Promise作为JavaScript异步编程的核心解决方案,自ES6规范发布以来已成为前端开发的标配。然而,许多开发者仅停留在API调用层面,对其内部实现机制缺乏深入理解。本文将通过手写一个符合Promise/A+规范的实现,系统解析Promise的核心原理,帮助读者掌握状态管理、链式调用、异步调度等关键技术点。
一、Promise核心机制解析
1.1 状态机模型实现
Promise规范定义了三种状态:pending、fulfilled和rejected。状态转换具有单向性,一旦从pending变为fulfilled/rejected,便不可更改。这种设计确保了异步操作的确定性。
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);
}
}
}
状态管理实现要点:
- 严格的状态检查(仅pending可转换)
- 回调队列的延迟执行机制
- 同步错误的捕获处理
1.2 链式调用实现原理
Promise的then方法支持链式调用,这依赖于返回值的新Promise实例创建。每个then调用都会返回一个新Promise,形成调用链。
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(() => {
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 if (this.state === 'pending') {
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;
}
链式调用关键点:
- 异步执行回调(通过setTimeout模拟微任务)
- 回调函数的错误捕获
- 新Promise的返回值处理
二、Promise/A+规范核心实现
2.1 返回值处理规范
根据Promise/A+规范,then方法的返回值需要特殊处理,可能返回普通值、Promise对象或抛出异常。
function resolvePromise(promise2, x, resolve, reject) {
// 防止循环引用
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
// 处理x为Promise的情况
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);
}
}
返回值处理要点:
- 循环引用检测
- thenable对象识别
- 多次调用保护
- 异常捕获机制
2.2 静态方法实现
Promise类提供了多个静态方法,其中Promise.resolve和Promise.reject是基础实现。
static resolve(value) {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
三、进阶功能实现
3.1 错误冒泡机制
未捕获的Promise拒绝需要特殊处理,现代浏览器会将其打印到控制台。
class MyPromise {
constructor(executor) {
// ...原有实现...
this.initUnhandledRejection();
}
initUnhandledRejection() {
const rejectError = this.reason;
if (this.state === 'rejected' &&
!this.onRejectedCallbacks.some(cb => cb.length > 0)) {
setTimeout(() => {
if (this.state === 'rejected') {
console.error('UnhandledPromiseRejectionWarning:', rejectError);
}
}, 0);
}
}
}
3.2 微任务队列模拟
由于浏览器环境限制,我们使用MutationObserver模拟微任务:
function asyncSchedule(fn) {
if (typeof MutationObserver !== 'undefined') {
const observer = new MutationObserver(fn);
const node = document.createElement('div');
observer.observe(node, { attributes: true });
node.setAttribute('x', 'y');
setTimeout(() => observer.disconnect(), 0);
} else {
setTimeout(fn, 0); // 降级方案
}
}
四、完整实现与测试
4.1 完整类实现
将上述模块整合后的完整实现:
class MyPromise {
// ...前文所有代码片段整合...
// 添加catch方法
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; })
);
}
// 静态all方法
static all(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let count = 0;
if (promises.length === 0) {
resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
},
reason => reject(reason)
);
});
});
}
}
4.2 规范测试用例
使用Promises/A+测试套件验证实现:
// 测试用例示例
describe('Promise基本功能', () => {
it('应该正确处理同步成功', () => {
const promise = new MyPromise(resolve => resolve(1));
return promise.then(val => expect(val).toBe(1));
});
it('应该支持链式调用', () => {
let order = [];
return new MyPromise(resolve => {
order.push(1);
resolve();
}).then(() => {
order.push(2);
return new MyPromise(resolve => resolve());
}).then(() => {
order.push(3);
expect(order).toEqual([1, 2, 3]);
});
});
});
五、实际应用建议
调试技巧:
- 在resolve/reject处添加日志
- 使用Promise.allSettled处理部分失败场景
- 实现自定义的Promise.retry方法
性能优化:
- 避免在then回调中创建不必要的Promise
- 合理使用Promise.race实现超时控制
- 批量处理并行Promise
错误处理最佳实践:
- 始终处理Promise拒绝
- 使用async/await改善可读性
- 实现全局未处理拒绝监听
// 全局未处理拒绝监听示例
window.addEventListener('unhandledrejection', event => {
console.warn('未处理的Promise拒绝:', event.reason);
// 可以在这里添加上报逻辑
});
通过手写Promise实现,开发者不仅能深入理解异步编程的核心机制,还能在实际项目中更灵活地运用Promise。建议结合ES6规范文档和实际业务场景,不断完善和优化自定义Promise实现。
发表评论
登录后可评论,请前往 登录 或 注册