手写Promise源码过Promise A+规范
适用人群
可熟练使用Promise
前言
关于手写Promise的源码之前尝试了不止4,5次但都以失败告终,不是觉得难不想坚持就是项目加急没有时间导致搁置。这次下定决心要花一个完整的时间段攻破这个事情,因为脑袋不是很灵光,总的算下来差不多4-5个小时,但是这里结果最重要,终于按照可以理解的思路完成了通过Promise A+规范的Promise代码编写!下面根据我的编写过程总结一下编码思路方便之后的理解。
准备工作
需要通过Promise A+规范,那就需要有测试用例来帮助我们判断自己写的的代码是否会经得住Promise A+的考验。所以我们可以准备一个专门用来检测Promise A+规范的npm包,来帮助我们判断编写的代码是否已经成功通过Promise A+规范。
添加promises-aplus-tests
包
npm包管理工具可以根据自己的习惯选取使用,这里使用yarn
复制yarn add promises-aplus-tests -D
修改package.json
中的scripts
"scripts": {
"test": "promises-aplus-tests MyPromise.js"
}
将MyPromise.js
换成自己写的代码文件,如果有文件夹嵌套需要带上路径。
这样我们就可以在认为代码编写完成后使用yarn test
测试自己的代码了。
Promise A+规范
既然我们写的代码要通过Promise A+规范,我们就得要了解规范里的内容是什么。关于规范相关的内容,可以在这里看👉👉👉https://promisesaplus.com/。因为不能一屏截出来,所以就不贴出来了,感兴趣的同学可以进去看。
Promise基本特征
我们先梳理一番自己对Promise的了解,便于后续的理解。
- promise函数对象的构造函数有一个回调函数作为参数,我们称为c0,回调函数会在函数执行时同步执行
- c0的参数中又有两个回调函数,我们称为c1,c2
- c1、c2会在c0中的我们指定的位置执行
- promise的初始状态为pending,内部的初始值为undefined
- c1执行后promise的状态会变为fulfilled,如果Promise有then方法,则then方法会被异步执行
- c2执行后promise的状态会变为rejected,如果Promise有catch方法,则catch方法会被异步执行
- c1、c2只会被执行一个
- then函数中的返回值可以任意值,也可以是新的promise,不可以是本promise,会报错提示
Chaining cycle detected for promise #<Promise>
- then函数中返回新的promise后,下面接着的then、catch会跟着新的promise的状态走
- 多个then函数在promise的状态转为fulfilled之后会依次异步执行
源码
executor的错误捕获没有处理
PENDING = "pending";
FULFILLED = "fulfilled";
REJECTED = "rejected";
class MyPromise {
state = PENDING;
result = void 0;
_resolveArr = [];
_rejectedArr = [];
constructor(executor) {
const resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.result = value;
this._resolveArr.forEach((func) => func());
}
};
const reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.result = reason;
this._rejectedArr.forEach((func) => func());
}
};
executor(resolve, reject);
}
_handleSettledFunc(promise, x, resolve, reject) {
// 检测循环调用
if (x === promise) {
reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
}
// then中返回新的promise时使用新promise状态。x为假值时忽略.then的判断
if (x && (typeof x === "object" || typeof x === "function")) {
let done = false;
// 捕获x.then及then.call的报错
try {
const then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (done) {
return;
}
done = true;
this._handleSettledFunc(promise, y, resolve, reject);
},
(r) => {
if (done) {
return;
}
done = true;
reject(r);
}
);
} else { // 没有报错直接resolve
resolve(x);
}
} catch (e) {
if (done) {
return;
}
done = true;
reject(e);
}
} else {
resolve(x);
}
}
/**
* 返回一个promise对象
* @param {function} onFulfilled resolve后需要执行的函数
* @param {function} onRejected reject后需要执行的函数
* @returns
*/
then(onFulfilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
const _handleFunc = (handleFunc) => {
// 只能在这使用setTimeout,否则promise变量不能传递,报未初始化的错
setTimeout(() => {
// 捕获handleFunc(this.result)中的报错
try {
this._handleSettledFunc(
promise,
handleFunc(this.result),
resolve,
reject
);
} catch (error) {
reject(error);
}
});
};
// 返回的函数会被异步调用
if (this.state === FULFILLED) {
_handleFunc(onFulfilled);
} else if (this.state === REJECTED) {
_handleFunc(onRejected);
} else {
// 数组形式保存onFulfilled、onRejected函数
this._resolveArr.push(() => _handleFunc(onFulfilled));
this._rejectedArr.push(() => _handleFunc(onRejected));
}
});
return promise;
}
}
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)