手写 promise ( 二 )根据A+规范手写 Promise
MyPromise
原生 Promise 基础用法
const p = new Promise((resolve,reject)=>{
resolve('嘿嘿 我成功了')
})
p.then(
res=>{
console.log('res',res)
// res 嘿嘿 我成功了
},
rej=>{
console.log('rej',rej)
}
)
const p1 = new Promise((resolve,reject)=>{
throw new Error("错误");
})
p.then(
res=>{
console.log('res',res)
},
rej=>{
console.log('rej',rej)
// rej 错误
}
)
MyPromise
- A+ 规范 2.1 得知 promise 有 3 个状态
- pending 可以转化为其他两个状态 fulfilled 、 rejected
- fulfilled 不能转化为 rejected 状态,rejected 同理
- 有 then 方法且可以连续 then
// 成功
const FULFILLED = "FULFILLED";
// 失败
const REJECTED = "REJECTED";
// 等待
const PENDING = "PENDING";
class MyPromise {
// effect executor 是执行器 第一次 new promise(fn) 的 fn 函数 , 默认直接执行
constructor(executor) {
// effect 成功函数
const resolve = (value) => {
// effect 避免失败后继续调用 resolve
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
// effect 失败函数
const reject = (reason) => {
// effect 避免成功后继续调用 reject
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
}
};
// mark 默认是 pending 状态
this.status = PENDING;
// mark 成功的值
this.value = undefined;
// mark 失败的原因
this.reason = undefined;
try {
// 默认会立刻执行
executor(resolve, reject);
} catch (error) {
// 如果调用执行器抛错了 默认调用 reject 方法
reject(error);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.status == FULFILLED) {
onFulfilled(this.value);
resolve(this.value);
}
if (this.status == REJECTED) {
onRejected(this.reason);
reject(this.reason);
}
});
}
}
module.exports = MyPromise;
此时的 MyPromise 已经满足连续 then 和 状态不能随意修改的条件了。
处理 new Promise 中有异步函数的情况
- 思路
- 在 MyPromise 内,定义两个队列用以存放 res rej 的函数。
- MyPromise 有三个状态,当执行 then 时,判断状态是否为 pending。
- 如果是 pending 说明是异步的。此时将 res rej 加入对应的函数队列中【onFulfilleds、onRejecteds】
- 因为 executor 函数是异步的,当执行 executor 中的 resolve 时,队列已经添加完毕,resolve 方法中遍历 onFulfilleds执行队列中的函数,reject 同理
案例
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('异步函数')
}, 1000);
});
p.then(
(res) => {
console.log("res", res);
},
(err) => {
console.log("err", err);
}
);
// 一秒后才执行结果
// res 异步函数
显然现在的 MyPromise 满足不了这样的条件,我们只需要加上亿点点条件即可。这里要用到发布订阅模式(不了解的小伙伴要自行查资料)。
优化
class MyPromise{
constructor(executor) {
...
// mark 利用发布订阅模式实现
// effect 成功回调函数
this.onFulfilleds = [];
// effect 失败回调函数
this.onRejecteds = [];
// effect 成功函数
const resolve = (value) => {
// effect 避免失败后继续调用 resolve
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 调用 resolve 则需要执行 异步 resolve 方法
this.onFulfilleds.forEach((fn) => fn());
}
};
// effect 失败函数
const reject = (reason) => {
// effect 避免成功后继续调用 reject
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
// 调用 reject 则需要执行 异步 reject 方法
this.onRejecteds.forEach((fn) => fn());
}
};
}
// mark then 方法有两个参数
then(onFulfilled, onRejected) {
// onFulfilled, onRejected 是可选参数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (data) => data;
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
return new MyPromise((resolve, reject) => {
...
// effect 说明是异步方法
if (this.status === PENDING) {
// mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
this.onFulfilleds.push(() => {
onFulfilled(this.value);
resolve(this.value)
});
this.onRejecteds.push(() => {
onRejected(this.reason);
reject(this.reason)
});
}
})
}
}
这样在 new MyPromise 时有异步任务在 then 时也会等到异步任务执行完毕,在执行 then 中的方法拿到成功或失败的信息。
捕获 then res 、rej 内的异常
案例
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步函数");
// throw new Error('错误')
}, 1000);
});
p.then(
(res) => {
console.log("res", res);
throw new Error("错误");
},
(err) => {
console.log("err", err);
}
).then(
(res) => {
console.log("res1", res);
},
(rej) => {
console.log("err1", rej);
}
);
优化
...
// mark then 方法有两个参数
then(onFulfilled, onRejected) {
// onFulfilled, onRejected 是可选参数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (data) => data;
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status == FULFILLED) {
// effect 这里加 try catch 为了 捕获 setTimeout 的异常【无法捕获异步方法的异常 exctor 的try catch 捕获不到异常】
try {
let x = onFulfilled(this.value);
resolve(this.value);
} catch (error) {
reject(error);
}
}
if (this.status == REJECTED) {
try {
let x = onRejected(this.reason);
reject(this.reason);
} catch (error) {
reject(error);
}
}
// effect 说明是异步方法
if (this.status === PENDING) {
// mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
this.onFulfilleds.push(() => {
try {
onFulfilled(this.value);
resolve(this.value);
} catch (error) {
reject(error);
}
});
this.onRejecteds.push(() => {
try {
onRejected(this.reason);
reject(this.reason);
} catch (error) {
reject(error);
}
});
}
});
return promise2;
}
then res 返回的是一个 promise
- 规范 2.3
- 在
then处理new MyPromise时 对resolve进行额外的判断。【resolvePromise】
案例
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步函数");
// throw new Error('错误')
}, 1000);
});
p.then(
(res) => {
console.log("res", res);
// throw new Error("错误");
return new Promise((resolve,reject)=>{
resolve('嘿嘿 我是新的 promise')
})
},
(err) => {
console.log("err", err);
}
).then(
(res) => {
console.log("res1", res);
},
(rej) => {
console.log("err1", rej);
}
);
// res 异步函数
// res1 嘿嘿 我是新的 promise
优化
// 成功
const FULFILLED = "FULFILLED";
// 失败
const REJECTED = "REJECTED";
// 等待
const PENDING = "PENDING";
// 因为所有的 promise 都遵循这个规范,规定这里这个写法应该兼容所有的 promise
const resolvePromise = (promise2, x, resolve, reject) => {
// 判断 x 的值和 promise2 是不是同一个值
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
let called;
// 判断 x 的类型
if (typeof x == "function" || (typeof x == "object" && x != null)) {
try {
// 取then 有可能这个then属性是通过 defineProperty来定义的
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
// 可能还是一个 promise 直到解析结果是一个普通值
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 说明是一个普通值
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 说明是一个普通值
resolve(x);
}
};
const rejectPromise = resolvePromise;
class MyPromise {
// effect executor 是执行器 第一次 new promise(fn) 的 fn 函数 , 默认直接执行
constructor(executor) {
// mark 利用发布订阅模式实现
// effect 成功回调函数
this.onFulfilleds = [];
// effect 失败回调函数
this.onRejecteds = [];
// effect 成功函数
const resolve = (value) => {
// effect 避免失败后继续调用 resolve
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 调用 resolve 则需要执行 异步 resolve 方法
this.onFulfilleds.forEach((fn) => fn());
}
};
// effect 失败函数
const reject = (reason) => {
// effect 避免成功后继续调用 reject
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
// 调用 reject 则需要执行 异步 reject 方法
this.onRejecteds.forEach((fn) => fn());
}
};
// mark 默认是 pending 状态
this.status = PENDING;
// mark 成功的值
this.value = undefined;
// mark 失败的原因
this.reason = undefined;
try {
// 默认会立刻执行
executor(resolve, reject);
} catch (error) {
// 如果调用执行器抛错了 默认调用 reject 方法
reject(error);
}
}
// mark then 方法有两个参数
then(onFulfilled, onRejected) {
// onFulfilled, onRejected 是可选参数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (data) => data;
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
// console.log('promise2',promise2);
if (this.status === FULFILLED) {
// effect 这里加 try catch 为了 捕获 setTimeout 的异常【无法捕获异步方法的异常 exctor 的try catch 捕获不到异常】
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
rejectPromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
// effect 说明是异步方法
if (this.status === PENDING) {
// mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
this.onFulfilleds.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejecteds.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
rejectPromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
}
// 延时方法 解决封装嵌套的问题
MyPromise.defer = MyPromise.deferred = () => {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise;
至此 MyPromise 已经可以通过 官方的测试案例啦!
后面继续完善 Promise 的各个 API。
PS
- 代码仓库地址觉得对你有帮助记得 🌟 Star 哦
- 后续会持续更新相关内容以及其他内容
- 觉得文章不错的话,记得 👍 🌟 嘻嘻
- 如有不足欢迎指正