一个符合 Promise/A+ 规范的 Promise
2019-04-11 本文已影响0人
天上月丶
// 表示状态,便于后期维护
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
// 因代码可能会异步执行,用于获取正确的this对象
const that = this;
// 初始状态为pending
that.state = PENDING;
// 用于保存resolve 或者 reject 中传入的值
that.value = null;
// 用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,
// 这时候应该把then中的回调保存起来用户状态改变时使用
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
// 首先两个函数都得判断当前状态是否是等待中,因为只有等待状态可以改变状态
// 将当前状态更改为对应状态,并且将传入的值赋值给value
// 遍历回调数组并执行
function resolve(value) {
// 首先需要判断传入的值是否为Promise类型
if(value instanceof MyPromise) {
return value.then(resolve, reject)
}
// 为了保证函数执行顺序,需要将函数整体代码用setTimeout包裹起来
setTimeout(() => {
if(that.state === PENDING) {
that.state = RESOLVED;
that.value = value;
that.resolvedCallbacks.map(cb => cb(that.value));
}
}, 0);
}
function reject(value) {
setTimeout(() => {
if(that.state === PENDING) {
that.state = REJECTED;
that.value = value;
that.rejectedCallbacks.map(cb => cb(that.value));
}
}, 0);
}
// 执行传入的参数并且将之前的两个函数当做参数传进去
// 注意: 可能执行函数过程中会遇到错误,需要捕获错误并执行reject函数
try{
fn(resolve, reject)
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
// 首先判断两个参数是否为函数类型,因为这两个参数时可选参数
// 当参数不是函数类型时,需要创建一个函数赋值给对应的参数,同时也实现了透传
// eg: Promise.resolve(4).then().then(value => console.log(value))
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r; };
let promise2;
// 判断状态
// 当状态不是等待态时,就去执行相应的函数。
// 如果状态是等待态的话,就往回调函数中push函数
if(that.state === PENDING) {
// 返回一个新的Promise对象,并在Promise中传入了一个函数
// 函数的基本逻辑和之前一样,往回调数组中push函数
// 同样,在执行函数的过程中可能会遇到错误,所以使用了try ... catch 包裹
// 规范规定,执行onFulfilled 或者 onRejected 函数时会返回一个X,
// 并且执行Promise解决过程,这是为了不同的Promise都可以兼容使用
return (promise2 = new MyPromise((resolve, reject) => {
that.resolvedCallbacks.push(() => {
try {
const x = onFulfilled(that.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
that.rejectedCallbacks.push(() => {
try {
const x = onRejected(that.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}));
}
if(that.state === RESOLVED) {
// 规范规定,传入的函数体需要异步执行
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
const x = onFulfilled(that.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}));
// onFulfilled(that.value);
}
if(that.state === REJECTED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
const x = onRejected(that.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}))
// onRejected(that.value)
}
function resolutionProcedure(promise2, x, resolve, reject) {
// 规范规定 x 不能与 promise2 相等,这样会发生循环引用的问题
if(promise2 === x) {
return reject(new TypeError('Error'));
}
// 如果x 为Promise 的话,需要判断以下几个情况
// 如果x处于等待态,Promise 需保持为等待态至 x 被执行或者拒绝
// 如果 x 处于其他状态,则用相同的值处理Promise
if(x instanceof MyPromise) {
x.then(function(value) {
resolutionProcedure(promise2, value, resolve, reject);
}, reject)
}
// 判断是否已经调用过函数
let called = false;
// 判断 x 是否为对象或者函数,如果都不是的话,将 x 传入 resolve 中
if(x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
// 如果 x 是对象或者函数的话,先把 x.then 赋值给 then, 然后再判断 then 的类型
let then = x.then;
if(typeof then === 'function') {
// 如果then 是函数类型的话,就将 x 作为函数的作用域 this 调用,并且传递两个回调函数作为参数,
// 第一个参数叫做 resolvePromise, 第二个参数为 rejectPromise, 两个回调函数都需要判断是否已经执行过函数,
// 然后进行相应的逻辑
then.call(x,
y => {
if(called) return;
called = true;
resolutionProcedure(promise2, y, resolve, reject);
},
e => {
if(called) return;
called = true;
reject(e);
}
)
// 如果不是函数类型的话,就将x传入 resolve 中
} else {
resolve(x);
}
} catch (e) {
// 以上代码执行的过程中如果抛错了,将错误传入 reject 函数中
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
};
eg :
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 0)
}).then(value => {
console.log(value)
});