简单的 promise
是什么?(thenable/PromiseLike
)
- 对象
- 有一个
then
方法
但是一个真正的 promise
除去这两点还有一些细化的东西,具体是一个怎么样的对象,then
方法内部是什么处理
javascript
function isPromiseLike(value) {
return (
value !== null &&
(typeof value === "object" || typeof value === "function") &&
typeof value.then === "function"
);
}
实现 PromiseA+ 规范
-
定义工具方法,用于
class
中的内部使用- 定义一个检测是否函数的方法
jsfunction isFunction(x) { return typeof x === "function"; }
- 定义一个判断是否
thenable
(PromiseLike
)的方法
scssfunction isThenable(x) { // 判断 x 是不是一个对象或者函数,并且 x.then 是一个函数 return ( ((typeof x === "object" && x !== null) || isFunction(x)) && isFunction(x.then) ); }
-
初始化一个
promise
构造函数,包含属性的定义,一个constructor
构造方法以及then
方法声明 定义内部变量,包含state
(promise
的状态),data
(完成状态下的数据),reason
(拒绝状态下的数据),settledhandlers
(完成状态下的回调函数)jsclass MyPromise { // Promise有三种状态 _state = "pending"; // 初始状态'pending' | 'fulfilled' | 'rejected' _data = undefined; // promise 完成状态下的相关数据 _reason = undefined; // promise 拒绝状态下的相关数据 _settledhandlers = []; // 当前 promise 完成状态下的回调函数,为什么要定义成一个数组,是因为可能会有多个 then 方法 constructor(executor) {} then(onFulfilled, onRejected) {} }
为什么这些属性没有设置私有
Promise/A+ 规范只要求状态不可变 ,没要求可见性 ; ES6 官方 Promise 把
[[PromiseState]]
做成引擎级私有槽,外部无法访问; 自己实现时,用#state
或WeakMap
把它设为私有,是最佳实践而非强制。
-
完善构造函数的实现
js// 构造函数接受一个执行器函数 executor constructor(executor) { // executor 必须是一个函数 if (!isFunction(executor)) { throw new TypeError(`Promise resolver ${executor} is not a function`); } // 定义实例属性 resolve 和 reject const resolve = (data) => { resolvePromise(this, data); }; // 这个简单 const reject = (err) => { rejectPromise(this, err); }; try { // 执行过程中有错误,会直接抛出错误 executor(resolve, reject); } catch (e) { return reject(e); } }
具体的
resolve
和reject
需要定义额外的方法去实现-
其中,
reject
方法考虑的内容较少,实现较为简单(只关注两件事,改变状态和更新reason
)jsfunction rejectPromise(prom, reason) { // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退 if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected // 直接拒绝,修改promise的状态 prom._state = "rejected"; prom._reason = reason; }
-
resolve
方法的实现,判断传入的值是不是一个promise
,要去吸收状态,否则直接改变promise
的状态jsfunction resolvePromise(prom, x) { // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退 if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected // 判断 x 是不是一个 promise(有可能是一个 thenable 对象) if (isThenable(x)) { // 判断 x 和 prom 是不是同一个对象 if (x === prom) { return rejectPromise( prom, new TypeError("Chaining cycle detected for promise #<Promise>") ); } // 不是同一个对象,要去吸收 x 的状态,还要增加一个 microtask 队列 queueMicrotask(() => { x.then( (data) => { // x 成功了,用 data 去 resolve prom resolvePromise(prom, data); // 递归解析 }, (err) => { // x 失败了,用 err 去 reject prom rejectPromise(prom, err); } ); }); } else { prom._state = "fulfilled"; prom._data = x; } }
-
-
实现
then
方法,就处理两个问题,回调什么时候调用,以及then
方法的返回新的promise
状态是什么jsthen(onFulfilled, onRejected) { // then 方法返回一个新的 promise const prom = new MyPromise((resolve, reject) => {}); // 为什么要定义成一个数组,是因为可能会有多个 then 方法 // 这里的回调函数要等到当前 promise 状态确定后才能调用 this._settledhandlers.push({ onFulfilled, onRejected, prom, // 当前 then 方法返回的 promise }); // 数组里面push的内容什么时候执行呢?当状态确定后执行 通过一个flushHandles函数 flushHandlers(this); return prom; }
flushHandlers
方法是依次执行promise
完成状态下的回调函数,通过then
方法添加js// 将curpromise的_settledhandlers数组中的回调函数依次执行 所有回调依次处理 function flushHandlers(curPromise) { if (curPromise._state === "pending") return; // 状态还是pending 直接返回 // 状态已经确定了 可能是fulfilled 也可能是rejected // 让这些回调函数依次执行 const settledhandlers = curPromise._settledhandlers; //取出所有的回调 // 将后续的处理放入微任务队列中执行 是将整个放入,不是循环一个一个放入 queueMicrotask(() => { while (settledhandlers.length) { // 这个prom 和传入的curPromise不一样,是then方法中new的那个promise // 取出第一个回调,需要清空原数组 const { onFulfilled, onRejected, prom } = settledhandlers.shift(); // 具体处理回调和prom的状态 // 判断,要是 onfocusfilled/onRejected 不是函数,就直接用当前promise的值去resolve/reject if (!isFunction(onFulfilled) && curPromise._state === "fulfilled") { // 状态穿透 resolvePromise(prom, curPromise._data); continue; // 继续下一个 } // 同样的,当前状态为失败,但是不是函数 if (!isFunction(onRejected) && curPromise._state === "rejected") { // 状态穿透 rejectPromise(prom, curPromise._reason); continue; // 继续下一个 } // 走到这里,说明是函数,那就运行其中一个回调,状态结果取决于回调的结果是否报错,因此要try catch let result; try { // 不报错就resolve; result = curPromise._state === "fulfilled" ? onFulfilled(curPromise._data) : onRejected(curPromise._reason); } catch (error) { // 报错就reject rejectPromise(prom, error); continue; // 继续下一个 } // 走到这里,说明没有报错,用result去resolve // 只要返回了一个结果,不管是成功还是失败,都要用这个结果去resolve resolvePromise(prom, result); } }); }
该方法为处理后续的回调,在状态确定后 或者调用
then
方法时执行因此需要额外在状态确定时调用一次,涉及到状态调用的地方有
rejectPromise
和resolvePromise
两个方法jsfunction rejectPromise(prom, reason) { // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退 if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected // 直接拒绝,修改promise的状态 prom._state = "rejected"; prom._reason = reason; // 状态确定后,去执行回调函数 flushHandlers(prom); } function resolvePromise(prom, x) { // 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退 if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected // 判断 x 是不是一个 promise(有可能是一个 thenable 对象) if (isThenable(x)) { xxxx..... } else { prom._state = "fulfilled"; prom._data = x; // 状态确定后,去执行回调函数 flushHandlers(prom); } }
-
调用测试,和
Promise
方法进行对比jsprocess.on("unhandledRejection", () => { console.log("process unhandledRejection"); }); const p = new Promise((resolve, reject) => { resolve(1); }); const p_my = new MyPromise((resolve, reject) => { resolve(1); }); console.log("start", p._state); const p1 = p.then(123).then(() => { return "p1 fulfilled"; }); const p2 = p_my.then(123).then(() => { return "p2 fulfilled"; }); setTimeout(() => { console.log("p=>", p); console.log("p_my=>", p_my); console.log("p1=>", p1); console.log("p2=>", p2); }, 0); // p=> Promise { 1 } // p_my=> MyPromise { // _state: 'fulfilled', // _data: 1, // _reason: undefined, // _settledhandlers: [] // } // p1=> Promise { 'p1 fulfilled' } // p2=> MyPromise { // _state: 'fulfilled', // _data: 'p2 fulfilled', // _reason: undefined, // _settledhandlers: [] // }
完整写法
js
// 贴合es6的写法
function isFunction(x) {
return typeof x === "function";
}
function isThenable(x) {
// 判断 x 是不是一个对象或者函数,并且 x.then 是一个函数
return (
((typeof x === "object" && x !== null) || isFunction(x)) &&
isFunction(x.then)
);
}
// 处理后续的回调 在状态确定后 或者 调用 then 方法执行
// 将curpromise的_settledhandlers数组中的回调函数依次执行 所有回调依次处理
function flushHandlers(curPromise) {
if (curPromise._state === "pending") return; // 状态还是pending 直接返回
// 状态已经确定了 可能是fulfilled 也可能是rejected
// 让这些回调函数依次执行
const settledhandlers = curPromise._settledhandlers; //取出所有的回调
// 将后续的处理放入微任务队列中执行 是将整个放入,不是循环一个一个放入
queueMicrotask(() => {
while (settledhandlers.length) {
// 这个prom 和传入的curPromise不一样,是then方法中new的那个promise
// 取出第一个回调,需要清空原数组
const { onFulfilled, onRejected, prom } = settledhandlers.shift();
// 具体处理回调和prom的状态
// 判断,要是 onfocusfilled/onRejected 不是函数,就直接用当前promise的值去resolve/reject
if (!isFunction(onFulfilled) && curPromise._state === "fulfilled") {
// 状态穿透
resolvePromise(prom, curPromise._data);
continue; // 继续下一个
}
// 同样的,当前状态为失败,但是不是函数
if (!isFunction(onRejected) && curPromise._state === "rejected") {
// 状态穿透
rejectPromise(prom, curPromise._reason);
continue; // 继续下一个
}
// 走到这里,说明是函数,那就运行其中一个回调,状态结果取决于回调的结果是否报错,因此要try catch
let result;
try {
// 不报错就resolve;
result =
curPromise._state === "fulfilled"
? onFulfilled(curPromise._data)
: onRejected(curPromise._reason);
} catch (error) {
// 报错就reject
rejectPromise(prom, error);
continue; // 继续下一个
}
// 走到这里,说明没有报错,用result去resolve
// 只要返回了一个结果,不管是成功还是失败,都要用这个结果去resolve
resolvePromise(prom, result);
}
});
}
function rejectPromise(prom, reason) {
// 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
// 直接拒绝,修改promise的状态
prom._state = "rejected";
prom._reason = reason;
// 状态确定后,去执行回调函数
flushHandlers(prom);
}
function resolvePromise(prom, x) {
// 当一个 promise 的状态确定后,状态和相关数据就不会再改变了,历史不可倒退
if (prom._state !== "pending") return; // 状态只能从 pending -> fulfilled/rejected
// 判断 x 是不是一个 promise(有可能是一个 thenable 对象)
if (isThenable(x)) {
// 判断 x 和 prom 是不是同一个对象
if (x === prom) {
return rejectPromise(
prom,
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
// 不是同一个对象,要去吸收 x 的状态,还要增加一个 microtask 队列
queueMicrotask(() => {
x.then(
(data) => {
// x 成功了,用 data 去 resolve prom
resolvePromise(prom, data); // 递归解析
},
(err) => {
// x 失败了,用 err 去 reject prom
rejectPromise(prom, err);
}
);
});
} else {
prom._state = "fulfilled";
prom._data = x;
// 状态确定后,去执行回调函数
flushHandlers(prom);
}
}
class MyPromise {
// Promise有三种状态
_state = "pending"; // 初始状态'pending' | 'fulfilled' | 'rejected'
_data = undefined; // promise 完成状态下的相关数据
_reason = undefined; // promise 拒绝状态下的相关数据
_settledhandlers = []; // 当前 promise 完成状态下的回调函数
// 构造函数接受一个执行器函数 executor
constructor(executor) {
// executor 必须是一个函数
if (!isFunction(executor)) {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
// 定义实例属性
const resolve = (data) => {
resolvePromise(this, data);
};
// 这个简单
const reject = (err) => {
rejectPromise(this, err);
};
try {
// 执行过程中有错误,会直接抛出错误
executor(resolve, reject);
} catch (e) {
return reject(e);
}
}
// 就处理两个问题,回调什么时候调用,以及 then 方法的返回新的 promise 状态是什么
then(onFulfilled, onRejected) {
// 这里的回调函数要等到当前 promise 状态确定后才能调用
const prom = new MyPromise((resolve, reject) => {});
// 为什么要定义成一个数组,是因为可能会有多个 then 方法
// 这里的回调函数要等到当前 promise 状态确定后才能调用
this._settledhandlers.push({
onFulfilled,
onRejected,
prom, // 当前 then 方法返回的 promise
});
// 数组里面push的内容什么时候执行呢?当状态确定后执行 通过一个flushHandles函数
flushHandlers(this);
return prom;
}
}