引言
Promise是JavaScript中处理异步操作的核心机制,它解决了回调地狱问题,提供了更优雅的异步代码编写方式。本文将从Promise的基本原理出发,逐步实现一个符合Promise/A+规范的Promise类,帮助你深入理解Promise的工作机制。
Promise基本原理
Promise的三种状态
Promise有三种状态,分别是:
- pending:初始状态,既不是成功也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
状态只能从pending转换为fulfilled或rejected,且一旦转换完成就不能再改变。
Promise的基本结构
一个基本的Promise实现需要包含:
- 状态管理
- 回调函数存储
- 状态转换逻辑
- then方法实现
从零开始实现Promise
1. 基础框架搭建
首先,我们创建一个Promise类,包含基本的状态管理和构造函数:
javascript
class MyPromise {
// 定义状态常量
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
// 初始状态为pending
this.status = MyPromise.PENDING;
// 存储成功的值
this.value = null;
// 存储失败的原因
this.reason = null;
// 存储成功回调函数队列
this.onFulfilledCallbacks = [];
// 存储失败回调函数队列
this.onRejectedCallbacks = [];
try {
// 执行器函数立即执行
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
// 执行器抛出异常时直接 reject
this.reject(error);
}
}
// 实现resolve方法
resolve(value) {
// 只有pending状态才能转换为fulfilled
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
// 异步执行所有成功回调
setTimeout(() => {
this.onFulfilledCallbacks.forEach(callback => {
callback(this.value);
});
}, 0);
}
}
// 实现reject方法
reject(reason) {
// 只有pending状态才能转换为rejected
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.reason = reason;
// 异步执行所有失败回调
setTimeout(() => {
this.onRejectedCallbacks.forEach(callback => {
callback(this.reason);
});
}, 0);
}
}
}
2. 实现then方法
then方法是Promise的核心,它接收两个参数:成功回调和失败回调,并返回一个新的Promise,实现链式调用:
javascript
then(onFulfilled, onRejected) {
// 确保onFulfilled和onRejected是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
// 返回新的Promise,实现链式调用
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
// 当前状态为fulfilled,异步执行成功回调
setTimeout(() => {
try {
const result = onFulfilled(this.value);
// 处理回调返回值,实现链式传递
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === MyPromise.REJECTED) {
// 当前状态为rejected,异步执行失败回调
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === MyPromise.PENDING) {
// 当前状态为pending,将回调存入队列
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
3. 实现resolvePromise方法
resolvePromise方法用于处理回调函数返回值,实现Promise链式调用的核心逻辑:
javascript
resolvePromise(promise, result, resolve, reject) {
// 如果返回值是当前Promise,抛出类型错误
if (promise === result) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 标记是否已经调用过resolve或reject
let called = false;
// 如果返回值是Promise实例
if (result instanceof MyPromise) {
if (result.status === MyPromise.PENDING) {
// 如果返回的Promise还是pending状态,等待其状态变化
result.then(value => {
this.resolvePromise(promise, value, resolve, reject);
}, reason => {
reject(reason);
});
} else {
// 如果返回的Promise已经有结果,直接使用其结果
result.then(resolve, reject);
}
}
// 如果返回值是对象或函数(可能是thenable对象)
else if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
try {
// 尝试获取then方法
const then = result.then;
if (typeof then === 'function') {
// 如果是thenable对象,调用其then方法
then.call(result, value => {
if (called) return;
called = true;
this.resolvePromise(promise, value, resolve, reject);
}, reason => {
if (called) return;
called = true;
reject(reason);
});
} else {
// 如果不是thenable对象,直接resolve
resolve(result);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 如果返回值是基本类型,直接resolve
resolve(result);
}
}
4. 实现catch方法
catch方法是then方法的语法糖,用于捕获Promise的错误:
javascript
catch(onRejected) {
return this.then(null, onRejected);
}
5. 实现finally方法
finally方法无论Promise状态如何都会执行,并且会将结果透传给下一个then:
javascript
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
reason => MyPromise.resolve(onFinally()).then(() => { throw reason })
);
}
6. 实现静态方法resolve
Promise.resolve方法返回一个以给定值解析后的Promise对象:
javascript
static resolve(value) {
// 如果value已经是Promise实例,直接返回
if (value instanceof MyPromise) {
return value;
}
// 如果value是thenable对象,转换为Promise
if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
try {
const then = value.then;
if (typeof then === 'function') {
return new MyPromise(then.bind(value));
}
} catch (error) {
return new MyPromise((resolve, reject) => reject(error));
}
}
// 基本类型值直接返回resolved状态的Promise
return new MyPromise(resolve => resolve(value));
}
7. 实现静态方法reject
Promise.reject方法返回一个带有拒绝原因的Promise对象:
javascript
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason));
}
8. 实现静态方法all
Promise.all方法接收一个Promise数组,返回一个新的Promise,当所有Promise都成功时才成功,有一个失败则立即失败:
javascript
static all(promises) {
return new MyPromise((resolve, reject) => {
// 检查输入是否是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('The input must be an array'));
}
const results = [];
let completed = 0;
const total = promises.length;
// 如果传入空数组,直接resolve
if (total === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
// 使用Promise.resolve处理非Promise值
MyPromise.resolve(promise).then(value => {
results[index] = value;
completed++;
// 所有Promise都完成时resolve
if (completed === total) {
resolve(results);
}
}, reason => {
// 有一个Promise失败就立即reject
reject(reason);
});
});
});
}
9. 实现静态方法race
Promise.race方法接收一个Promise数组,返回一个新的Promise,其结果由第一个完成的Promise决定:
javascript
static race(promises) {
return new MyPromise((resolve, reject) => {
// 检查输入是否是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('The input must be an array'));
}
// 如果传入空数组,保持pending状态
if (promises.length === 0) {
return;
}
promises.forEach(promise => {
// 使用Promise.resolve处理非Promise值
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
10. 实现静态方法allSettled
Promise.allSettled方法等待所有Promise完成(无论成功或失败),并返回每个Promise的结果:
javascript
static allSettled(promises) {
return new MyPromise((resolve) => {
// 检查输入是否是数组
if (!Array.isArray(promises)) {
return resolve([{ status: 'rejected', reason: new TypeError('The input must be an array') }]);
}
const results = [];
let completed = 0;
const total = promises.length;
// 如果传入空数组,直接resolve
if (total === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise)
.then(value => {
results[index] = { status: 'fulfilled', value };
})
.catch(reason => {
results[index] = { status: 'rejected', reason };
})
.finally(() => {
completed++;
if (completed === total) {
resolve(results);
}
});
});
});
}
完整代码实现
将以上所有部分整合,得到完整的Promise实现:
javascript
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
setTimeout(() => {
this.onFulfilledCallbacks.forEach(callback => {
callback(this.value);
});
}, 0);
}
}
reject(reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.reason = reason;
setTimeout(() => {
this.onRejectedCallbacks.forEach(callback => {
callback(this.reason);
});
}, 0);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === MyPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
this.resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
resolvePromise(promise, result, resolve, reject) {
if (promise === result) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
if (result instanceof MyPromise) {
if (result.status === MyPromise.PENDING) {
result.then(value => {
this.resolvePromise(promise, value, resolve, reject);
}, reason => {
reject(reason);
});
} else {
result.then(resolve, reject);
}
} else if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
try {
const then = result.then;
if (typeof then === 'function') {
then.call(
result,
value => {
if (called) return;
called = true;
this.resolvePromise(promise, value, resolve, reject);
},
reason => {
if (called) return;
called = true;
reject(reason);
}
);
} else {
resolve(result);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(result);
}
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
reason => MyPromise.resolve(onFinally()).then(() => { throw reason })
);
}
static resolve(value) {
if (value instanceof MyPromise) {
return value;
}
if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
try {
const then = value.then;
if (typeof then === 'function') {
return new MyPromise(then.bind(value));
}
} catch (error) {
return new MyPromise((resolve, reject) => reject(error));
}
}
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason));
}
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('The input must be an array'));
}
const results = [];
let completed = 0;
const total = promises.length;
if (total === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(value => {
results[index] = value;
completed++;
if (completed === total) {
resolve(results);
}
}, reason => {
reject(reason);
});
});
});
}
static race(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('The input must be an array'));
}
if (promises.length === 0) {
return;
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
static allSettled(promises) {
return new MyPromise((resolve) => {
if (!Array.isArray(promises)) {
return resolve([{ status: 'rejected', reason: new TypeError('The input must be an array') }]);
}
const results = [];
let completed = 0;
const total = promises.length;
if (total === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise)
.then(value => {
results[index] = { status: 'fulfilled', value };
})
.catch(reason => {
results[index] = { status: 'rejected', reason };
})
.finally(() => {
completed++;
if (completed === total) {
resolve(results);
}
});
});
});
}
}
Promise使用示例
基本用法
javascript
// 创建Promise
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
// 或者 reject(new Error('Failed'));
}, 1000);
});
// 使用Promise
promise
.then(value => {
console.log('成功:', value);
return value + ' - 链式调用';
})
.then(value => {
console.log('链式调用结果:', value);
})
.catch(reason => {
console.log('失败:', reason);
})
.finally(() => {
console.log('无论成功失败都会执行');
});
静态方法使用示例
javascript
// Promise.all
const promise1 = MyPromise.resolve(1);
const promise2 = MyPromise.resolve(2);
const promise3 = MyPromise.resolve(3);
MyPromise.all([promise1, promise2, promise3])
.then(values => console.log('all results:', values)) // [1, 2, 3]
.catch(reason => console.log('all error:', reason));
// Promise.race
const promiseA = new MyPromise(resolve => setTimeout(() => resolve('A'), 100));
const promiseB = new MyPromise(resolve => setTimeout(() => resolve('B'), 50));
MyPromise.race([promiseA, promiseB])
.then(value => console.log('race result:', value)) // 'B'
.catch(reason => console.log('race error:', reason));
// Promise.allSettled
const promiseX = MyPromise.resolve('X');
const promiseY = MyPromise.reject(new Error('Y failed'));
MyPromise.allSettled([promiseX, promiseY])
.then(results => console.log('allSettled results:', results));
// [
// { status: 'fulfilled', value: 'X' },
// { status: 'rejected', reason: Error('Y failed') }
// ]
Promise/A+规范测试
为了验证我们实现的Promise是否符合标准,可以使用Promise/A+规范测试套件:
javascript
// 安装测试套件
// npm install promises-aplus-tests -D
// 添加测试文件promise-test.js
const promisesAplusTests = require('promises-aplus-tests');
const adapter = {
deferred() {
const promise = new MyPromise((resolve, reject) => {
return {
resolve,
reject
};
});
return {
promise,
resolve: promise.resolve,
reject: promise.reject
};
}
};
promisesAplusTests(adapter, function (err) {
// 所有测试完成
console.log(err);
});
常见问题与注意事项
1. Promise状态不可逆
一旦Promise状态从pending变为fulfilled或rejected,就不能再改变:
javascript
const promise = new MyPromise((resolve, reject) => {
resolve('Success');
reject(new Error('Failed')); // 此调用无效
});
promise.then(value => console.log(value)); // 'Success'
2. 异步执行特性
Promise的回调函数是异步执行的,即使立即resolve:
javascript
console.log('Start');
const promise = new MyPromise(resolve => {
console.log('Executor');
resolve('Success');
});
promise.then(value => console.log(value));
console.log('End');
// 输出顺序:
// Start
// Executor
// End
// Success
3. 错误冒泡
Promise链中的错误会一直向后传递,直到被catch捕获:
javascript
new MyPromise((resolve, reject) => {
resolve();
})
.then(() => {
throw new Error('Error in then');
})
.then(() => {
console.log('This will not execute');
})
.catch(reason => {
console.log('Catch error:', reason); // 捕获前面抛出的错误
});
总结
通过手动实现Promise,我们深入理解了其内部工作机制,包括状态管理、异步处理、链式调用等核心概念。Promise的设计思想非常优雅,它通过将回调函数规范化,解决了异步代码的可读性和可维护性问题。
掌握Promise不仅有助于我们更好地使用JavaScript异步编程,也能帮助我们理解更高级的异步模式,如async/await。希望本文能帮助你彻底掌握Promise,并在实际项目中灵活运用。
扩展学习
- async/await语法糖与Promise的关系
- Promise性能优化技巧
- 手写Promise并发控制
- Promise在实际项目中的最佳实践
- 浏览器与Node.js中的Promise实现差异