手写 Promise 全功能实现,超越原生 API
2022-12-18 01:02:12
手写 Promise,超越原生 API
异步编程的利器
在 JavaScript 的世界中,Promise 就像一把锋利的宝剑,助你轻松驾驭异步编程的汪洋大海。原生 Promise API 固然强大,但偶尔也会捉襟见肘,无法满足一些特定的需求。为了突破瓶颈,我们不妨亲自动手,打造一个更强大的 Promise 实现,探索异步编程的更多可能。
Promise 的本质
Promise 的本质很简单,它就像一个处于等待、已解决或已拒绝三种状态之中的容器。当一个异步操作启动时,它将被 Promise 收入囊中,静静等待结果。若操作成功,Promise 就会将其结果打包带走,化身已解决状态。反之,若操作失败,Promise 也会将失败原因收录其中,成为已拒绝状态。
手写 Promise 的基本实现
想要打造自己的 Promise,我们首先要搭建它的基本框架。一个 Promise 构造函数和一个 then 方法足以满足基本的异步处理需求。
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// resolve 和 reject 函数
const resolve = (value) => {
// 确保只执行一次
if (this.state !== 'pending') return;
this.state = 'resolved';
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => callback(value));
};
const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach((callback) => callback(reason));
};
// 执行 executor 函数
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 返回一个新的 Promise
return new Promise((resolve, reject) => {
if (this.state === 'resolved') {
// 使用 setTimeout 异步执行回调
setTimeout(() => {
try {
// 获取 onFulfilled 的返回值
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === 'rejected') {
setTimeout(() => {
try {
// 获取 onRejected 的返回值
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
}, 0);
} else {
// 等待状态,将回调加入队列
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
reject(reason);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
}
}
完善 Promise 的功能
为了让我们的 Promise 更加强大,我们可以继续添加更多实用的方法。
- All 方法: 处理多个 Promise,直到所有 Promise 都已解决或有一个 Promise 被拒绝。
Promise.all = (promises) => {
// 返回一个新的 Promise
return new Promise((resolve, reject) => {
const results = [];
let pendingCount = promises.length;
// 遍历每个 Promise
promises.forEach((promise, index) => {
promise.then((value) => {
results[index] = value;
pendingCount--;
if (pendingCount === 0) {
resolve(results);
}
}, reject);
});
});
};
- AllSettled 方法: 处理多个 Promise,无论它们是解决还是拒绝,都返回一个已解决的状态。
Promise.allSettled = (promises) => {
// 返回一个新的 Promise
return new Promise((resolve) => {
const results = [];
let pendingCount = promises.length;
// 遍历每个 Promise
promises.forEach((promise, index) => {
promise.then(
(value) => {
results[index] = { status: 'fulfilled', value };
pendingCount--;
if (pendingCount === 0) {
resolve(results);
}
},
(reason) => {
results[index] = { status: 'rejected', reason };
pendingCount--;
if (pendingCount === 0) {
resolve(results);
}
}
);
});
});
};
- Any 方法: 处理多个 Promise,只要有一个 Promise 解决就返回该解决的结果,否则返回拒绝的结果。
Promise.any = (promises) => {
// 返回一个新的 Promise
return new Promise((resolve, reject) => {
let rejectedCount = 0;
// 遍历每个 Promise
promises.forEach((promise) => {
promise.then(resolve, (reason) => {
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError('All promises were rejected'));
}
});
});
});
};
- Race 方法: 处理多个 Promise,只要有一个 Promise 解决或拒绝,就返回该结果。
Promise.race = (promises) => {
// 返回一个新的 Promise
return new Promise((resolve, reject) => {
// 遍历每个 Promise
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
};
- Reject 方法: 直接返回一个已拒绝状态的 Promise。
Promise.reject = (reason) => {
// 返回一个新的 Promise
return new Promise((_, reject) => {
reject(reason);
});
};
- Resolve 方法: 直接返回一个已解决状态的 Promise。
Promise.resolve = (value) => {
// 返回一个新的 Promise
return new Promise((resolve) => {
resolve(value);
});
};
使用手写的 Promise
使用我们手写的 Promise 与使用原生的 Promise 非常相似,只需直接替换原生 Promise 的 API 即可。
// 创建一个 Promise 对象
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello, world!');
}, 1000);
});
// 使用 then 方法处理异步操作的结果
promise.then((value) => {
console.log(value); // 输出: Hello, world!
}, (reason) => {
console.log(reason); // 输出: Oops, something went wrong!
});
结论
手写 Promise 的过程不仅是一次技术练习,更是一次对异步编程思想的深刻理解。通过打造自己的 Promise 实现,我们不仅扩展了 JavaScript 工具箱,也拓展了我们对异步编程的认知边界。
常见问题解答
-
手写 Promise 与原生 Promise 有什么区别?
手写 Promise 提供了更多的灵活性,可以根据特定需求进行定制,并添加额外的功能。
-
手写 Promise 会比原生 Promise 慢吗?
这取决于具体的实现,但在大多数情况下,两者之间的性能差异可以忽略不计。
-
何时应该使用手写的 Promise?
当原生 Promise 无法满足需求时,或者当需要对 Promise 行为进行更精细的控制时,可以使用手写的 Promise。
-
手写 Promise 会被未来版本的 JavaScript 取代吗?
不太可能,因为 Promise 已经成为 JavaScript 异步编程的基石,手写 Promise 提供了一种更灵活的方式来使用它们。
-
如何确保手写的 Promise 正确无误?
进行单元测试、编写文档并遵循最佳实践可以帮助确保手写的 Promise 稳定可靠。