实现 Promise 的核心功能:
- 实例方法:then/catch/finally;
- 静态方法 resolve/reject/race/all/allSettled/any;
- 跑通 PromiseA+ 单元测试
Table of contents
Open Table of contents
constrauctor
- 定义类 MyPromise, 内部添加构造函数 constructor, 构造函数需要接收回调函数 func
- 在构造函数中定义 resolve 和 reject
- 构造函数内部调用 func 并将 resolve 和 reject 传入:func(resolve,reject)
// 1. 定义类
class MyPromise {
// 2. 添加构造函数
constructor(func) {
// 3. 定义 resolve/reject
const resolve = value => {
console.log("resolve", value);
};
const reject = reason => {
console.log("reject", reason);
};
// 4. 指定回调函数
func(resolve, reject);
}
}
state
- 定义 3 个常量用来保存状态,pending,fulfilled,rejected
- MyPromise 内部定义属性 state 和 result 分别用来保存状态和原因
- 调用 resolve 时传入具体原因,如果状态为 pending 则更改状态并记录兑现原因
- 调用 reject 时传入具体原因,如果状态为 pending 则更改状态并记录拒绝原因
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
// 1. 添加状态
state = PENDING;
// 2. 添加原因
result = undefined;
constructor(func) {
// 3. 调整 resolve/reject
// 4. 状态不可逆
const resolve = value => {
if (this.state !== PENDING) return;
// 改状态
this.state = FULFILLED;
// 记录原因
this.result = reason;
console.log("resolve", value);
};
const reject = reason => {
if (this.state !== PENDING) return;
// 改状态
this.state = REJECTED;
// 记录原因
this.result = reason;
};
func(resolve, reject);
}
}
实例方法
then
- 添加 then 方法,接收 2 个回调函数:
- 成功回调 onFulfilled
- 失败回调 onRejected
- 判断传入的 onFulfilled 和 onRejected 是否为函数,如果不是设置默认值
- 根据状态调用 onFulfilled 或 onRejected 并传入兑现或拒绝原因
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
state = PENDING;
result = undefined;
constructor(func) {
const resolve = value => {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.result = reason;
console.log("resolve", value);
};
const reject = reason => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.result = reason;
};
func(resolve, reject);
}
// 1. 添加实例方法
then(onfulfilled, onrejected) {
// 2. 参数判断
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
// 2.1 执行成功/失败回调
if (this.state === FULFILLED) {
onfulfilled(this.result);
} else if (this.state === REJECTED) {
onrejected(this.result);
}
}
}
异步和多次调用
-
MyPromise 添加私有属性#handlers
- 保存 then 方法调用时状态为 pending 的回调函数
- 格式为对象数组:[{onFulfilled,onRejected}…]
-
构造函数内部调整 resolve 和 reject 的逻辑:
- 调用 resolve 时取出数组#handlers 中的所有 onFulfilled 回调函数进行调用
- 调用 reject 时取出数组#handlers 中的所有 onRejected 回调函数进行调用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
state = PENDING;
result = undefined;
// 1. 定义实例属性
#handlers = [];
constructor(func) {
const resolve = value => {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.result = value;
// 3. 调用成功回调
this.#handlers.forEach(h => {
h.onfulfilled(this.result);
});
};
const reject = result => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.result = result;
// 3. 调用失败回调
this.#handlers.forEach(h => {
h.onrejected(this.result);
});
};
func(resolve, reject);
}
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
if (this.state === FULFILLED) {
onfulfilled(this.result);
} else if (this.state === REJECTED) {
onrejected(this.result);
} else if (this.state === PENDING) {
// 2. 保存回调函数
this.#handlers.push({
onfulfilled: () => {
onfulfilled(this.result);
},
onrejected: () => {
onrejected(this.result);
},
});
}
}
}
异步任务 - 函数封装
-
封装执行异步任务的函数
-
定义函数传入异步任务(回调函数)
-
内部根据实际情况判断并使用开启异步任务的 api 即可,比如 queueMicrotask,MutationObserver
-
调用的 api 可以根据实际情况进行调整
-
如果都无法执行,使用 setTimeout 兜底
-
-
调整 then 中的逻辑,fulFilled,rejected,pending3 种状态时的回调函数,使用封装的函数包装一次
// 1. 定义函数
function runAsyncTask(callback) {
// 2. 调用核心 api 们
if (typeof queueMicrotask === "function") {
queueMicrotask(callback);
} else if (typeof MutationObserver === "function") {
const observer = new MutationObserver(callback);
const textNode = document.createTextNode("");
observer.observe(textNode, { characterData: true });
textNode.data = "1";
} else if (typeof setTimeout === "function") {
setTimeout(callback, 0);
} else {
throw new Error("No async task available");
}
}
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
state = PENDING;
result = undefined;
#handlers = [];
constructor(func) {
const resolve = value => {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.result = value;
this.#handlers.forEach(h => {
h.onfulfilled(this.result);
});
};
const reject = result => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.result = result;
this.#handlers.forEach(h => {
h.onrejected(this.result);
});
};
func(resolve, reject);
}
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
// 3. 使用封装函数
if (this.state === FULFILLED) {
runAsyncTask(() => {
onfulfilled(this.result);
});
} else if (this.state === REJECTED) {
runAsyncTask(() => {
onrejected(this.result);
});
} else if (this.state === PENDING) {
this.#handlers.push({
onfulfilled: () => {
runAsyncTask(() => {
onfulfilled(this.result);
});
},
onrejected: () => {
runAsyncTask(() => {
onrejected(this.result);
});
},
});
}
}
}
链式编程
fulfilled
- 链式编程的本质 then 方法会返回一个新的 MyPromise 对象
- 将原本的代码迁移到返回的 MyPromise 对象的回调函数中
- 内部通过 try-catch 捕获异常,出现异常通过 reject 传递异常
- 获取 onFulfilled 的执行结果,并通过 resolve 传递
/**
* 链式编程-处理异常和普通内容
*/
class MyPromise {
// ...
//* 1. 返回新的 promise 实例
//* 2. 获取返回值
//* 2.1 处理返回值
//* 2.2 处理异常
//*
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
// 1. 返回新的 promise 实例
const p2 = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsyncTask(() => {
// 2. 获取返回值
try {
const x = onfulfilled(this.result);
if (x === p2) {
throw new TypeError("Chaining cycle detected for promise");
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (e) {
reject(e);
}
});
} else if (this.state === REJECTED) {
runAsyncTask(() => {
onrejected(this.result);
});
} else if (this.state === PENDING) {
this.#handlers.push({
onfulfilled: () => {
runAsyncTask(() => {
onfulfilled(this.result);
});
},
onrejected: () => {
runAsyncTask(() => {
onrejected(this.result);
});
},
});
}
});
return p2;
}
}
reject
- 判断 onRejected 可能出现的异常,如果出现通过 reject 传递
- 获取 onRejected 函数的执行结果
- 将 fulfilled 状态时的处理逻辑抽取为函数,rejected 状态时调用函数复用逻辑
// 3. 抽取函数
function resolvePromise(p2, x, resolve, reject) {
if (x === p2) {
throw new TypeError("Chaining cycle detected for promise #<Promise>");
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
class MyPromise {
// ...
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
const p2 = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsyncTask(() => {
try {
const x = onfulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (this.state === REJECTED) {
runAsyncTask(() => {
// 1. 处理异常
try {
// 2. 获取返回值
const x = onrejected(this.result);
// 4. 调用函数
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === PENDING) {
this.#handlers.push({
onfulfilled: () => {
runAsyncTask(() => {
onfulfilled(this.result);
});
},
onrejected: () => {
runAsyncTask(() => {
onrejected(this.result);
});
},
});
}
});
return p2;
}
}
pending
- then 方法中 pending 状态时推入数组的函数增加 try-catch 捕获异常
- 获取推入数组的回调函数的返回值
- 调用上一节封装的函数并传入获取的值
function resolvePromise(p2, x, resolve, reject) {
if (x === p2) {
throw new TypeError("Chaining cycle detected for promise #<Promise>");
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
class MyPromise {
// ...
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : v => v;
onrejected =
typeof onrejected === "function"
? onrejected
: e => {
throw e;
};
const p2 = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
if (this.state === FULFILLED) {
runAsyncTask(() => {
try {
const x = onfulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (this.state === REJECTED) {
runAsyncTask(() => {
try {
const x = onrejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === PENDING) {
this.#handlers.push({
onfulfilled: () => {
runAsyncTask(() => {
// 1. 处理异常
try {
// 2. 获取返回值
const x = onfulfilled(this.result);
// 调用函数
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
onrejected: () => {
runAsyncTask(() => {
try {
const x = onrejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
});
}
}
});
return p2;
}
}
catch
-
定义 catch 方法,接收拒绝的回调函数 onRejected
-
catch 方法的本质是内部调用 then 方法
-
调用形式为第一个回调函数传入 undefined, 第二个回调函数传入 onRejected 即可
class MyPromise {
// ...
constructor(func) {
// ...
// 2. 处理异常
try {
func(resolve, reject);
} catch (error) {
reject(error);
}
}
catch(onrejected) {
// 1. 内部调用 then
return this.then(null, onrejected);
}
}
finally
- 添加 finally 方法,接收最终执行的回调函数 onFinally
- finally 方法的本质为内部调用 then 方法
- 调用形式为第一个和第二个回调函数均传入 onFinally 即可
class MyPromise {
// ...
finally(onfinally) {
return this.then(onfinally, onfinally);
}
}
静态方法
resolve
-
通过 static 关键字添加静态方法 resolve, 接收参数 value
-
内部判断传入的值
- 如果是 Promise 实例,直接返回
- 其他的值,创建 Promise 实例并返回,内部通过 resolve(value) 传递 value
class MyPromise {
// ...
static resolve(value) {
// 1. 判断传入值 Promise 直接返回
if (value instanceof MyPromise) return value;
// 2. 转为 Promise 并返回
return new MyPromise(resolve => {
resolve(value);
});
}
}
reject
- 添加静态方法 reject 并接收参数 value
- 内部返回一个拒绝状态的 Promise 实例即可
class MyPromise {
// ...
// 1. 返回 rejected 状态的 Promise
static reject(value) {
return new MyPromise((_, reject) => {
reject(value);
});
}
}
race
- 添加静态方法 race 接收参数 promises
- 内部返回一个新的 Promise 实例,在返回的 Promise 实例中:
- 判断参数是否为数组,不是通过 reject 传递错误
- 遍历 Promise 数组,通过 resolve 静态方法等待每一个兑现
- 任何一个兑现,调用 resolve 传递兑现结果
- 任何一个拒绝,调用 reject 传递拒绝原因
class MyPromise {
// ...
static race(promises) {
// 1. 返回 Promise
return new MyPromise((resolve, reject) => {
// 2. 判断是否为数组
if (!Array.isArray(promises))
return reject(new TypeError("promises must be an array"));
// 等待第一个敲定
promises.forEach(p => {
MyPromise.resolve(p).then(resolve, reject);
});
});
}
}
all
- 添加静态方法 all
- 内部返回 Promise 实例,在返回的 Promise 实例中:
- 判断参数是否为数组,不是通过 reject 传递错误
- 空数组直接以空数组为结果进行兑现
- 遍历 Promise 数组,通过 resolve 静态方法等待结果
- 处理全部兑现:
- 通过数组记录结果,用索引的方式来添加,目的是保证结果的顺序和 Promise-数组的顺序一致
- 通过兑现次数进行判断,因为是通过索引的方式记录结果,如果第一次兑现的是最后一个,那么数组的长度就已经和 Promise 数组的长度一致了,所以需要通过兑现次数来进行判断
- 任意一个拒绝,调用 reject 传递拒绝原因
- 处理全部兑现:
class MyPromise {
// ...
static all(promises) {
// 1. 返回 Promise
return new MyPromise((resolve, reject) => {
// 2. 判断是否为数组
if (!Array.isArray(promises))
return reject(new TypeError("promises must be an array"));
// 3. 空数组直接兑现
promises.length === 0 && resolve([]);
// 4.1 记录结果
const result = [];
let count = 0;
promises.forEach((p, index) => {
MyPromise.resolve(p).then(
res => {
// result.push 无法保证结果的顺序和 promises 数组的顺序一致
// index 和 promises 的索引一致,保证顺序
result[index] = res;
// 4.2 判断全部兑现
count++;
if (count === promises.length) resolve(result);
},
// 5. 处理第一个拒绝
reject
);
});
});
}
}
allSettled
-
添加静态方法 allSettled
-
内部返回 Promise 实例,在返回的 Promise 实例中:
- 判断参数是否为数组,不是通过 reject 传递错误
- 空数组直接以空数组为结果进行兑现
-
遍历 Promise 数组,通过 resolve 静态方法等待敲定结果
-
等待全部敲定:并记录结果,根据兑现和拒绝将如下格式的内容通过索引的方式的记录到数组中
-
处理兑现:{state:FULFILLED,value:‘xxx’}
-
处理拒绝:{state:REJECTED,reason:‘xxx’}
-
-
根据敲定的次数判断是否全部敲定,全部敲定之后,通过 resolve 传递结果数组
class MyPromise {
// ...
static allsettled(promises) {
// 1. 返回 promise
return new MyPromise(resolve => {
// 2. 数组判断
if (!Array.isArray(promises))
return resolve(new TypeError("promises must be an array"));
// 3. 为空直接敲定
promises.length === 0 && resolve([]);
// 4. 等待全部敲定
// 4.1 记录结果
const result = [];
let count = 0;
promises.forEach((p, index) => {
MyPromise.resolve(p)
.then(
res => {
// 4.2 处理兑现
result[index] = { status: "fulfilled", value: res };
},
err => {
// 处理拒绝
result[index] = { status: "rejected", reason: err };
}
)
.finally(() => {
count++;
if (count === promises.length) resolve(result);
});
});
});
}
}
any
- 添加静态方法 any
- 内部返回 Promise 实例,在返回的 Promise 实例中:
- 判断参数是否为数组,不是通过 reject 传递错误
- 空数组直接以空数组为结果进行兑现
- 遍历 Promise 数组,通过 resolve 静态方法等待结果
- 第一个兑现,通过 resolve 传递兑现结果
- 全部拒绝:
- 定义数组,保存拒绝原因,通过索引记录,目的是保证顺序和 Promise 数组一致
- 通过次数判断是否全部拒绝,当全部拒绝时,通过 reject 传递 AggregateError 类型的错误,并将拒绝原因数组传递进去即可
class MyPromise {
// ...
static any(promises) {
// 1. 返回 promise, 数组判断
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises))
return reject(new TypeError("promises must be an array"));
// 2. 为空直接拒绝
promises.length === 0 &&
reject(new AggregateError(promises, "All promises were rejected"));
// 3. 等待结果
const errors = [];
let count = 0;
promises.forEach((p, index) => {
MyPromise.resolve(p).then(
// 3.1 第一个兑现
resolve,
err => {
// 3.2 全部拒绝
errors[index] = err;
count++;
if (count === promises.length)
reject(new AggregateError(errors, "All promises were rejected"));
}
);
});
});
}
}
Promise A+ 标准单元测试
promises-aplus-tests
- 提供 deferred 方法,返回对象{promise,resolve,reject}
- 1.1 promise: pending 状态的 promise 实例(自己手写的 Promise)
- 1.2 resolve: 以传入的原因兑现 promise
- 1.3 reject: 以传入的原因拒绝 promise
module.exports = {
deferred() {
const res = {};
res.promise = new MyPromise((resolve, reject) => {
res.resolve = resolve;
res.reject = reject;
});
return res;
},
};