根据Promise/A+规范实现 Promise
amandakelake opened this issue · 4 comments
amandakelake commented
整篇promise的实现不是我原创,是我研究了群里某位大佬以及一些前人的实现,在自己理解的基础上,记录下来,本篇文章更多是为自己服务
注释我写成英文了(具体缘由暂时不说了)
相信有能力理解promise的程序员都有阅读英文的能力(暂时没有也建议掌握这项能力)
// three states
const PENGING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// promise accepts a function argument that will execute immediately.
function MyPromise(fn) {
const _this = this;
_this.currentState = PENDING;
// the value of Promise
_this.value = undefined;
// To save the callback of `then`,only cached when the state of the promise is pending,
// for the new instance returned by `then`, at most one will be cached
_this.resolvedCallbacks = [];
_this.rejectedCallbacks = [];
_this.resolve = (value) => {
if (value instanceof MyPromise)
// If value is a Promise, execute recursively
return value.then(_this.resolve, _this.reject)
}
// execute asynchronously to guarantee the execution order
setTimeout(() => {
if (_this.currentState === PENDING) {
_this.currentState = RESOLVED;
_this.value = value;
_this.resolvedCallbacks.forEach(cb => cb());
}
})
}
_this.reject = (reason) => {
setTimeout(() => {
if (_this.currentState === PENGING) {
_this.currentState = REJECTED;
_this.value = reason;
_this.rejectedCallbacks.forEach(cb => cb());
}
})
}
// to solve the following problem
// new Promise(() => throw Error('error))
try {
fn(_this.resolve, _this.reject);
} catch (e) {
_this.reject(e);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
const self = this;
// specification 2.2.7, `then` must return a new promise
let promise2;
// specification 2.2, both `onResolved` and `onRejected` are optional arguments
// it should be ignored if `onResolved` or `onRjected` is not a function, which implements the penetrate pass of it's value
// Promise.resolve(4).then().then((value) => console.log(value))
onResolved = typeof onResolved === 'function' ? onResolved : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => throw r;
if (self.currentState === RESOLVED) {
return (promise2 = new MyPromise((resolve, reject) => {
// specification 2.2.4, wrap them with `setTimeout`, in order to insure that `onFulfilled` and `onRjected` execute asynchronously,
setTimeout(() => {
try {
let x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
}
if (self.currentState === REJECTED) {
return (promise2 = new MyPromise((resolve, reject) => {
// execute `onRejected` asynchronously
setTimeout(() => {
try {
let x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}))
}
if (self.currentState === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
self.resolvedCallbacks.push(() => {
// Considering that it may throw error, wrap them with `try/catch`
try {
let x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
self.rejectedCallbacks.push(() => {
try {
let x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
})
}))
}
}
// specification 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
// specification 2.3.1,`x` and `promise2` can't refer to the same object, avoiding the circular references
if (promise2 === x) {
return reject(new TypeError('Error'));
}
// specification 2.3.2, if `x` is a Promise and the state is `pending`, the promise must remain, If not, it should execute.
if (x instanceof MyPromise) {
if (x.currentState === PENDING) {
// call the function `resolutionProcedure` again to confirm the type of the argument that x resolves
// If it's a primitive type, it will be resolved again to pass the value to next `then`.
x.then((value) => {
resolutionProcedure(promise2, value, resolve, reject);
}, reject)
} else {
x.then(resolve, reject);
}
return;
}
// specification 2.3.3.3.3
// if both `reject` and `resolve` are executed, the first successful execution takes precedence, and any further executions are ignored
let called = false;
// specification 2.3.3, determine whether `x` is an object or a function
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// specification 2.3.3.2, if can't get `then`, execute the `reject`
try {
// specification 2.3.3.1
let then = x.then;
// if `then` is a function, call the `x.then`
if (typeof then === 'function') {
// specification 2.3.3.3
then.call(x, y => {
if (called) return;
called = true;
// specification 2.3.3.3.1
resolutionProcedure(promise2, y, resolve, reject);
}, e => {
if (called) return;
called = true;
reject(e);
});
} else {
// specification 2.3.3.4
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// specification 2.3.4, `x` belongs to primitive data type
resolve(x);
}
}
julytang commented
你已经使用箭头函数了,没必要用_this指向this了
You have already used the arrow function, so it's unnecessary to define _this
amandakelake commented
@julytang 谢谢指正
我原来很多地方是用ES5语法写的 所以为了保险才加上去的 现在的确可以去掉了
julytang commented
@amandakelake 博客很棒,整理的很完整,点赞,加油
amandakelake commented
@julytang 谢谢鼓励
路漫漫其修远兮 还有很多要学的