手动实现 promise
要手动实现一个简单版的 Promise
,需要理解它的基本结构和工作原理。Promise
是一个用于处理异步操作的对象,主要有三种状态:
- pending(等待态):初始状态,既没有被解决也没有被拒绝。
- fulfilled(已解决态):操作成功完成。
- rejected(已拒绝态):操作失败。
当 Promise
状态变为 fulfilled
或 rejected
时,它不能再次改变。同时,Promise
还支持链式调用,通过 .then()
和 .catch()
来注册回调函数处理不同状态。
下面是一个简单的 Promise
实现:
手动实现 Promise
js
class MyPromise {
constructor(executor) {
// 初始化状态
this.state = 'pending'; // Promise 初始状态为 pending
this.value = undefined; // 成功时返回的值
this.reason = undefined; // 失败时返回的原因
// 用于存储成功与失败回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 定义 resolve 方法
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'; // 将状态改为成功
this.value = value; // 保存成功的值
// 执行所有成功的回调函数
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 定义 reject 方法
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'; // 将状态改为失败
this.reason = reason; // 保存失败的原因
// 执行所有失败的回调函数
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 执行传入的 executor 函数
try {
executor(resolve, reject);
} catch (error) {
reject(error); // 如果执行过程中发生异常,直接 reject
}
}
// then 方法实现
then(onFulfilled, onRejected) {
// 如果没有传入 onRejected,则默认抛出 reason
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
// 如果没有传入 onFulfilled,则默认返回 value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// 返回一个新的 Promise,支持链式调用
return new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步执行,保证 then 函数在当前事件循环结束后执行
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result); // 处理成功回调
} catch (error) {
reject(error); // 捕获回调中的异常
}
});
} else if (this.state === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
} else if (this.state === 'pending') {
// 如果 Promise 还在 pending 状态,将回调函数存起来
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
});
}
});
}
// catch 方法实现,实际上就是 then 的语法糖
catch(onRejected) {
return this.then(null, onRejected);
}
}
解释实现原理:
状态管理:
Promise
的状态只能从pending
变为fulfilled
或rejected
,并且一旦状态改变后就不能再次改变。- 使用
this.state
来管理Promise
的当前状态。
回调存储:
- 在
Promise
处于pending
状态时,then
方法中的成功或失败回调并不会立即执行。因此,我们用两个数组onFulfilledCallbacks
和onRejectedCallbacks
分别存储回调,等到状态改变后再依次执行它们。
- 在
异步处理:
- 根据 Promise 规范,
then
中的回调函数必须异步执行。因此,使用setTimeout
来模拟异步行为,使得回调不会在当前事件循环中立即执行。
- 根据 Promise 规范,
链式调用:
- 每次调用
then
方法都会返回一个新的Promise
,以便进行链式调用。 - 通过
try...catch
捕获回调函数中的错误,如果出现错误,直接调用reject
。
- 每次调用
catch 方法:
catch
是then
的简化版本,用于处理reject
的情况,相当于then(null, onRejected)
。
使用示例:
js
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 1000);
});
p.then((value) => {
console.log(value); // 输出: 成功
return '继续链式调用';
}).then((value) => {
console.log(value); // 输出: 继续链式调用
}).catch((err) => {
console.error(err);
});
这段代码展示了如何手动实现一个简化版的 Promise
,并支持异步操作、状态管理和链式调用。
手动实现 Promise.all
要手动实现一个类似于 Promise.all
的功能,可以创建一个函数来接收一组 promise,并返回一个新的 promise。这个新 promise 只有在所有输入的 promise 都成功时才会成功,如果任何一个 promise 失败,它将立即失败。我们可以使用 Promise
的基本原理来构建这个逻辑。
下面是手动实现 Promise.all
的示例:
javascript
function promiseAll(promises) {
return new Promise((resolve, reject) => {
// 检查传入的是否为一个数组
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
let resolvedCount = 0; // 记录成功的 promise 数量
const results = []; // 存储每个 promise 的结果
const promisesLength = promises.length;
// 如果传入的数组为空,立即 resolve 一个空数组
if (promisesLength === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
// 确保每个项是一个 promise,使用 Promise.resolve 处理
Promise.resolve(promise)
.then((value) => {
// 将结果保存到对应的索引位置
results[index] = value;
resolvedCount++;
// 当所有的 promise 都 resolve 时,resolve 整个结果数组
if (resolvedCount === promisesLength) {
resolve(results);
}
})
.catch((error) => {
// 一旦有任何一个 promise 失败,立即 reject
reject(error);
});
});
});
}
// 使用示例
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));
const promise3 = Promise.resolve(42);
promiseAll([promise1, promise2, promise3])
.then((results) => console.log(results)) // 输出: [3, 'foo', 42]
.catch((error) => console.log(error));
解释:
- 参数检查:首先,检查传入的参数是否为数组。如果不是,直接
reject
一个TypeError
。 - Promise 处理:
- 我们使用
Promise.resolve
来确保每个项都是一个 promise(即便它可能是一个普通值)。 - 每次
promise
成功时,我们将结果存储在results
数组中,保持与输入数组相同的索引位置。
- 我们使用
- 计数:当每个 promise 成功时,
resolvedCount
增加。当这个计数等于输入 promise 的总数时,表示所有 promise 都已经成功,最终resolve
整个结果数组。 - 失败处理:如果有任何一个 promise 失败,立即调用
reject
,并终止整个操作。
这个实现完全模仿了 Promise.all
的行为。