手撕 Promise
Promise/A+规范
术语
promise一个有 then 方法的对象或函数,行为符合本规范thenable一个定义了 then 方法的对象或函数value任何JavaScript的合法值exceptionthrow语句抛出的值reason一个标识promise被拒绝的原因的值
promise 的状态
pending只能由pending状态转换为其他两个状态fulfilled带value,不能再改变状态了。rejected带reason,不能再改变状态了。
then 方法
then 方法有两个可选参数
onFulfilled, 在promise完成后被调用,onRejected, 在promise被拒绝执行后调用- 只被调用一次
then 方法可以被调用多次 then 方法必须返回一个 promise,实现链式调用
promise 的解析过程
// promise的解析过程
function resolvePromise(promise, x) {
// x 不允许与 promise 相同
if (promise === x) {
rejectedPromise(promise, new TypeError("cant be the same"));
return;
}
// x 是 promise的情况下,根据x的状态决定promise的状态
if (isPromise(x)) {
if (x.status === statusMap.FULFILLED) {
fulfilledPromise(promise, x.value);
return;
}
if (x.status === statusMap.REJECTED) {
rejectedPromise(promise, x.reason);
return;
}
// 如果x还是pending状态,则需要等待x执行完毕,再决定promise的装忒
if (x.status === statusMap.PENDING) {
x.then(
() => {
fulfilledPromise(promise, x.value);
},
() => {
rejectedPromise(promise, x.reason);
}
);
return;
}
return;
}
// 如果x不是promise,是一个函数或者对象
if (isObject(x) || isFunction(x)) {
let then;
let called = false;
try {
then = x.then;
} catch (error) {
// 如果x上没有then方法(即不符合Promise/A+规范),直接reject
rejectedPromise(promise, error);
return;
}
// 如果有then方法,需要保证then方法只能被执行一次。(通过called)
if (isFunction(then)) {
try {
// 执行then方法
then.call(
x,
(y) => {
if (called) {
return;
}
// 设置标签位为执行过
called = true;
// 设置promise的状态
resolvePromise(promise, y);
},
(r) => {
if (called) {
return;
}
called = true;
rejectedPromise(promise, r);
}
);
} catch (error) {
// 执行异常直接抛出错误
if (called) {
return;
}
called = true;
rejectedPromise(promise, error);
}
return;
} else {
// 有then属性,但是then不是可执行函数,那么认为x就是当前的返回值
fulfilledPromise(promise, x);
return;
}
} else {
// x不是对象或者函数,是某个具体值,直接fulfilled
fulfilledPromise(promise, x);
return;
}
}
具体实现
// 步骤一:了解promise规范
// 步骤二:实现
// 步骤三:测试
const statusMap = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
// 将promise设置为fulfilled状态
function fulfilledPromise(promise, value) {
// 只能从pending状态转换为其他状态
if (promise.status !== statusMap.PENDING) {
return;
}
promise.status = statusMap.FULFILLED;
promise.value = value;
runCbs(promise.fulfilledCbs, value);
}
// 将promise设置为rejected状态
function rejectedPromise(promise, reason) {
// 只能从pending状态转换为其他状态
if (promise.status !== statusMap.PENDING) {
return;
}
promise.status = statusMap.REJECTED;
promise.reason = reason;
runCbs(promise.rejectedCbs, reason);
}
function runCbs(cbs, value) {
cbs.forEach((cb) => cb(value));
}
function isFunction(fn) {
return (
Object.prototype.toString.call(fn).toLocaleLowerCase() ===
"[object function]"
);
}
function isObject(obj) {
return (
Object.prototype.toString.call(obj).toLocaleLowerCase() ===
"[object object]"
);
}
function isPromise(p) {
return p instanceof Promise;
}
class Promise {
constructor(fn) {
this.status = statusMap.PENDING;
this.value = undefined;
this.reason = undefined;
this.fulfilledCbs = []; // then fulfilled callback
this.rejectedCbs = []; // then rejected callback
fn(
(value) => {
// 防止直接传进来一个thenalbe
// fulfilledPromise(this, value);
resolvePromise(this, value);
},
(reason) => {
rejectedPromise(this, reason);
}
);
}
// 两个参数
then(onFulfilled, onRejected) {
const promise1 = this;
const promise2 = new Promise(() => {});
if (promise1.status === statusMap.FULFILLED) {
if (!isFunction(onFulfilled)) {
return promise1;
}
setTimeout(() => {
try {
const x = onFulfilled(promise1.value);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
}
if (promise1.status === statusMap.REJECTED) {
if (!isFunction(onRejected)) {
return promise1;
}
setTimeout(() => {
try {
const x = onRejected(promise1.reason);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
}
if (promise1.status === statusMap.PENDING) {
onFulfilled = isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value;
};
onRejected = isFunction(onRejected)
? onRejected
: (err) => {
throw err;
};
promise1.fulfilledCbs.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(promise1.value);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
});
promise1.rejectedCbs.push(() => {
setTimeout(() => {
try {
const x = onRejected(promise1.reason);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
});
}
return promise2;
}
}
// 测试用到的钩子
Promise.deferred = function () {
const deferred = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
};
module.exports = Promise;