Promise 重写
重写过程 MyPromise
- new Promise((resolve, reject) 中执行器函数立即执行,所以将 executor 函数放在类 MyPromise 的构造器函数中并且立即执行,同时,给出 promise 的三个状态常量
const PENDING = "PENDING",
FULFILLED = "FULFILLED",
REJECTED = "REJECTED";
class MyPromise {
constructor(executor) {
executor(resolve, reject);
}
}
- 一个 promise 初始状态为 pending,同时 resolve 和 reject 函数的方法参数不确定
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
executor(resolve, reject);
}
}
- 构造器函数中声明 resolve 和 reject 保证每次使用的都是独立的函数, 并且函数中只有 pengding 状态才可以执行函数,同时将传进来的值给到实例对象
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = (reason) => {
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
executor(resolve, reject);
}
- 在类中写一个原型上的方法 then , 根据状态执行对应的方法, 同时使用实例对象上的属性 this.value
class MyPromise {
// 构造器函数中声明 resolve 和 reject 保证每次使用的都是独立的函数
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
}
};
executor(resolve, reject);
}
// 直接写在原型上的 then 方法中根据状态执行对应的方法, 同时使用实例对象上的属性 this.value
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
}
}
- 在抛出错误时会出现错误, 要用 try…catch 包裹一下
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
// 出现错误就直接执行 reject 函数
try {
executor(resolve, reject);
} catch (e) {
reject(e)
}
}
- 当执行器函数中出现异步情况时, 如果 resolve 或者 reject 函数异步执行, 当执行到 then 方法时会是 pending 状态, 此时无法正常执行回调函数, 为了解决这个问题, 在构造器中设置两个容器, 分别用来存放成功的回调和失败的回调, 使用发布-订阅模式来先收集所有的等待执行函数, 等到 then 也执行完毕后再执行相应的 resolve 或者 reject 函数
class MyPromise {
// 构造器函数中声明 resolve 和 reject 保证每次使用的都是独立的函数
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
// 设置两个容器, 分别用来存放成功的回调函数和失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 发布
this.onFulfilledCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 发布
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
// 出现错误就直接执行 reject 函数
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 直接写在原型上的 then 方法中根据状态执行对应的方法, 同时使用实例对象上的属性 this.value
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
if (this.status === PENDING) {
// 订阅
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
let promise = new MyPromise((resolve, reject) => {
// resolve('success')
// reject('error')
// throw new Error('exception: error')
setTimeout(() => {
resolve("success 2");
}, 2000);
});
promise.then(
(value) => {
console.log("value1", value);
},
(reason) => {
console.log("reason1", reason);
}
);
promise.then(
(value) => {
console.log("value2", value);
},
(reason) => {
console.log("reason2", reason);
}
);
promise 的链式调用
- 链式调用的结果由 return 决定
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((res) => {
return "world";
}).then((res) => {
console.log(res); // world
});
- 通过 return 新的 Promise 来决定传递值
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
return new Promise((resolve, reject) => {
resolve("world");
});
}).then((value) => {
console.log(value); // world
});
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
return new Promise((resolve, reject) => {
reject("error");
});
}).then(
(value) => {
console.log("value", value);
},
(reason) => {
console.log("reason", reason); // reason error
}
);
- 链式调用中没有 return 返回值则默认返回 undefined 并且再下一次链式调用中默认走 onFulfilled 方法
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
return new Promise((resolve, reject) => {
reject("error");
});
})
.then(
(value) => {
console.log("value", value);
},
(reason) => {
console.log("reason", reason); // reason error
// 无返回值默认 return undefined;
}
)
.then(
(value) => {
console.log("---", value); // --- undefined
},
(reason) => {
console.log("+++", reason);
}
);
- 在 then 中抛出错误会在下一次链式调用的 onRejected 方法
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
throw new Error("throw error");
}).then(
(value) => {
console.log("---", value);
},
(reason) => {
console.log("+++", reason); // +++ throw error
}
);
- 链式调用使用 onRejected 和 catch 捕获异常,哪个离得近就被哪个捕获到
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
throw new Error("throw error");
})
.then(
(value) => {
console.log("---", value); // --- undefined
},
(reason) => {
console.log("then", reason); // then Error: throw error
}
)
.catch((err) => {
console.log("+++", err);
});
- 在 catch 后继续链式调用可以获取到 catch 返回的值, 和正常的一样, 成功走 onFulfilled 方法, 失败走 onRejected 方法
let p = new Promise((resolve, reject) => {
resolve("hello");
});
p.then((value) => {
throw new Error("throw error");
})
.then((value) => {
console.log("---", value); // --- undefined
})
.catch((err) => {
console.log("+++", err); // +++ Error: throw error
return "catch msg";
})
.then(
(value) => {
console.log("111", value); // 111 catch msg
},
(reason) => {
console.log("222", reason);
}
);
链式调用中成功的条件
then return 普通的 JavaScript value
then return 新的 promise 成功态的结果 value
失败的条件
then return 新的 promise 失败态的原因 reason
then 抛出了异常 throw new Error
promise 能够链式调用的原理和实现
原理: 每一次的链式调用都会返回一个新的 promise 对象
class MyPromise {
// 构造器函数中声明 resolve 和 reject 保证每次使用的都是独立的函数
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
// 设置两个容器, 分别用来存放成功的回调函数和失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 发布
this.onFulfilledCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 发布
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
// 出现错误就直接执行 reject 函数
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 直接写在原型上的 then 方法中根据状态执行对应的方法, 同时使用实例对象上的属性 this.value
then(onFulfilled, onRejected) {
// 给出默认值, then 中没有参数时就当他为一个以参数传值的函数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 为了能够对 x 值进行处理, 由于函数执行完毕之前获取不到 promise2 ,所以这里使用异步
setTimeout(() => {
try {
// 完成一个成功或者失败函数返回一个值, 通过对这个值进行处理来返回一个新的promise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
// 订阅
this.onFulfilledCallbacks.push(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
catch(errCallback) {
return this.then(null, errCallback);
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 规范一: promise2 和 x 不能指向相同的对象, 即在链式调用中, 如果第一次then的返回值是被赋值的本身, 那显然是错误的
if (promise2 === x) {
return reject(
new TypeError(
"then方法返回的promise和在then中进行resolve或者reject后返回的x不能指向同一个对象"
)
);
}
// 规范三: 如果 resolve 和 reject 被重复多次调用, 只执行第一次后面的忽略
let called = false;
// 规范三: 对于返回来的 x 值的类型的判定, 如果是一个对象或者函数, 还需要判断 x 中的 then 属性的类型, 如果 then 是一个函数, 那么就认定 x 为一个 promise(MyPromise) 对象; 如果不是一个函数, 那么认定 x 不为一个 promise(MyPromise) 对象, 直接调用 resolve 方法
if ((typeof x === "object" && x !== null) || typeof x === "function") {
// 考虑到可能对象被 defineProperty 修改过 get 操作从而抛出错误, 使用try...catch包裹
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
// 当发送的是一个 promise 对象时, 会再继续找到其中的 resolve 中真正的值
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
resolve 函数中的实参仍是 MyPromise 对象时,需要对 then 方法中获取到的 value 进行判断
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给 undefined
this.value = undefined;
this.reason = undefined;
// 设置两个容器, 分别用来存放成功的回调函数和失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
// 如果这里传进来的是一个 MyPromise 对象
if (value instanceof MyPromise) {
value.then(resolve, reject)
return;
}
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 发布
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 发布
this.onRejectedCallbacks.forEach(fn => fn());
}
}
// 出现错误就直接执行 reject 函数
try {
executor(resolve, reject);
} catch (e) {
reject(e)
}
}
完成静态方法 resolve 和 reject
// 静态方法
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error)
})
}
完成 MyPromise.all() 方法
设计思路:
- 一个静态方法
- 接受数据是一个数组, 数组中可能出现 MyPromise 也可能出现普通数据, 针对不同进行处理
static all(promiseArr) {
// 使用 idx 记录数量值是因为当数组中不在第一位的数据出现时, 前面的位置会出现 empty 空数据, 如果使用 resArr.length 就会不准
let resArr = [],
idx = 0;
return new MyPromise((resolve, reject) => {
promiseArr.map((promise, index) => {
if (isPromise(promise)) {
promise.then((res) => {
formatResArr(res, index, resolve);
}, reject)
} else {
formatResArr(promise, index, resolve);
}
})
})
function formatResArr(value, index, resolve) {
resArr[index] = value;
if (++idx === promiseArr.length) {
resolve(resArr)
}
}
function isPromise(x) {
if ((typeof x === 'object' && x !== null) || typeof (x) === 'function') {
let then = x.then;
return typeof then === 'function';
}
return false
}
}
function getP(val, delay = 0) {
return new MyPromise((resolve, reject) => {
if (delay) {
setTimeout(() => {
resolve(val);
}, delay);
} else {
resolve(val);
}
});
}
let pp1 = getP(123);
let pp2 = getP("shaoyahu", 2000);
let pp3 = getP([1, 2, 3]);
let pp4 = getP({ name: "shaoyahu" });
let pp = MyPromise.all([pp1, pp2, pp3, pp4, "zhangsan"]);
pp.then((res) => console.log(res));
完成 MyPromise.allSettled() 方法
将 isPromise 方法提到外面去一起使用
class MyPromise {
// 构造器函数中声明 resolve 和 reject 保证每次使用的都是独立的函数
constructor(executor) {
// 给上初始状态PENDING
this.status = PENDING;
// 函数 resolve 和 reject 参数默认值给undefined
this.value = undefined;
this.reason = undefined;
// 设置两个容器, 分别用来存放成功的回调函数和失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
// 如果这里传进来的是一个 MyPromise 对象
if (value instanceof MyPromise) {
value.then(resolve, reject);
return;
}
// 只有 pengding 状态才可以执行
if (this.status == PENDING) {
this.status = FULFILLED;
this.value = value;
// 发布
this.onFulfilledCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
// 只有 pengding 状态才可以执行
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 发布
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
// 出现错误就直接执行 reject 函数
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 直接写在原型上的 then 方法中根据状态执行对应的方法, 同时使用实例对象上的属性 this.value
then(onFulfilled, onRejected) {
// 给出默认值, then 中没有参数时就当他为一个以参数传值的函数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 为了能够对 x 值进行处理, 由于函数执行完毕之前获取不到 promise2 ,所以这里使用异步
setTimeout(() => {
try {
// 完成一个成功或者失败函数返回一个值,通过对这个值进行处理来返回一个新的promise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
// 订阅
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
catch(errCallback) {
return this.then(null, errCallback);
}
// 静态方法
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error);
});
}
static all(promiseArr) {
// 使用 idx 记录数量值是因为当数组中不在第一位的数据出现时, 前面的位置会出现 empty 空数据, 如果使用 resArr.length 就会不准
let resArr = [],
idx = 0;
return new MyPromise((resolve, reject) => {
promiseArr.map((promise, index) => {
if (isPromise(promise)) {
promise.then((res) => {
formatResArr(res, index, resolve);
}, reject);
} else {
formatResArr(promise, index, resolve);
}
});
});
function formatResArr(value, index, resolve) {
resArr[index] = value;
// 等所有 promise 都执行完毕了就 resolve 回原来的数组
if (++idx === promiseArr.length) {
resolve(resArr);
}
}
}
static allSettled(promiseArr) {
let resArr = [],
idx = 0;
// 判断一下传入的值是不是一个可迭代对象
if (!isIterable(promiseArr)) {
throw new TypeError(`${promiseArr}不是一个可迭代对象`);
}
return new MyPromise((resolve, reject) => {
if (promiseArr.length === 0) {
resolve([]);
}
// 对每一个 promise 都进行执行, 成功或者失败返回相类似的格式就行, 执行的方法还是由传入决定
promiseArr.map((promise, index) => {
if (isPromise(promise)) {
promise.then(
(value) => {
formatResArr("fulfilled", value, index, resolve);
},
(reason) => {
formatResArr("rejected", reason, index, resolve);
}
);
} else {
formatResArr("fulfilled", promise, index, resolve);
}
});
});
function formatResArr(status, value, index, resolve) {
switch (status) {
case "fulfilled":
resArr[index] = {
status,
value,
};
break;
case "rejected":
resArr[index] = {
status,
reason: value,
};
break;
default:
break;
}
// 等所有 promise 都执行完毕了就 resolve 回原来的数组
if (++idx === promiseArr.length) {
resolve(resArr);
}
}
}
}
function isPromise(x) {
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let then = x.then;
return typeof then === "function";
}
return false;
}
function isIterable(value) {
return (
value !== null &&
value !== undefined &&
typeof value[Symbol.iterator] === "function"
);
}
function getP(val, delay = 0) {
return new MyPromise((resolve, reject) => {
if (delay) {
setTimeout(() => {
resolve(val);
}, delay);
} else {
resolve(val);
}
});
}
let pp5 = new MyPromise((resolve, reject) => {
reject("lisi");
});
let pp1 = getP(123);
let pp2 = getP("shaoyahu", 2000);
let pp = MyPromise.allSettled([pp1, pp2, pp5, "zhangsan"]);
pp.then((res) => console.log(res));
完成 MyPromise.race() 方法
一共只有一个新的 promise, 在新 promise 中对数组中每个 promise 直接传入新 promise 的 resolve 和 reject 方法,当一个被触发时,新的 promise 就结束了,所以其他的 promise 就不会在继续了
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.map((promise) => {
//按照顺序一个一个来进行 then ,该怎么执行怎么执行, 只要有一个结束了, 当前这个新 new 出来的 MyPromise 对象就直接执行相应的 resolve 或者 reject 方法结束掉所有 promise
if (isPromise(promise)) {
// 该怎么执行怎么执行
promise.then(resolve, reject);
} else {
resolve(promise);
}
})
})
}
完成 MyPromise.finally() 方法
finally() 特点
- 无论外面的 promise 成功还是失败都要走, 并且回调不带参数
- 正常走 finally 之后的 then 或者 catch
- 如果 finally 内部有 promise并且有延时处理, 整个 finally 会等待
- 如果两个都是成功, 取外面的成功的结果
- 如果两个都是失败, 取里面失败的结果
- 如果外面是成功, 里面是失败, 取里面的失败的结果
- 如果外面是失败, 里面是成功, 取外面失败的结果
Promise.resolve("promsie resolve")
.finally(() => {
console.log("finally");
return new Promsie((resolve, reject) => {
setTimeout(() => {
reject("new Promise errer");
});
});
})
.then((res) => {
console.log("success:" + res);
})
.catch((err) => {
console.log("catch:" + err);
});
重写
finally(finallyCallback) {
return this.then((value) => {
// 外面成功
// 1.里面成功, 直接走 then 将外面成功的 value 传给下一个 then
// 2.里面失败, finallyCallback 方法中失败, 直接被最外面的 catch 捕获到输出里面的失败结果
return MyPromise.resolve(finallyCallback()).then(() => value)
}, (reason) => {
// 外面失败
// 1.里面成功, 直接抛出外面的错误 reason 到最外面的 catch 中被捕获
// 2.里面失败, finallyCallback 方法中失败, 直接被最外面的 catch 捕获到输出里面的失败结果
return MyPromise.resolve(finallyCallback()).then(() => {
throw reason
})
})
}
实现 promisify
一个方法, 返回的是一个 promise 对象, 需要使用 then 来获取成功的数据
直接包裹住就好了
static promisify(fn) {
return (...args) => {
return new MyPromise((resolve, reject) => {
fn(...args, (error, data) => {
if (error) {
return reject(error)
}
resolve(data)
})
})
}
}
const fs = require("fs");
const MyPromise = require("./MyPromise");
const read = MyPromise.promisify(fs.readFile);
read("../测试文本文件/one.txt", "utf-8").then((res) => console.log(res));
实现 promisifyAll
就是使用 promisify 方法用遍历将一个集合中的所有都包一层
static promisifyAll(fns) {
Object.keys(fns).map(fnName => {
if (typeof (fns[fnName] === 'function')) {
fns[fnName + 'Async'] = MyPromise.promisify(fns[fnName]);
}
})
return fns;
}
const fs = require("fs");
const MyPromise = require("./MyPromise");
const fsAsync = MyPromise.promisifyAll(fs);
fsAsync
.readFileAsync("../测试文本文件/one.txt", "utf-8")
.then((res) => console.log(res));