class Promise {
state = "pending"; // pending,fulfilled,rejected
value = undefined;
callbackStack = [];
constructor(fn) {
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
// 2.1.2.1
if (this.state !== "pending") return;
this.state = "fulfilled";
this.value = value;
setTimeout(() => {
this.callbackStack.forEach(fn => fn());
}, 0);
}
reject(reason) {
// 2.1.3.1
if (this.state !== "pending") return;
this.state = "rejected";
this.value = reason;
setTimeout(() => {
this.callbackStack.forEach(fn => fn());
}, 0);
}
then(onFulfilled, onRejected) {
let self = this;
// 穿透 2.2.7.3 2.2.7.4
if (typeof onFulfilled !== "function") onFulfilled = x => x;
if (typeof onRejected !== "function") {
onRejected = x => {
throw x;
};
}
let promise2;
promise2 = new Promise(function(resolve, reject) {
// 2.2.4
setTimeout(() => {
self.handle(promise2, onFulfilled, onRejected, resolve, reject);
});
});
return promise2;
}
handle(promise2, onFulfilled, onRejected, resolve, reject) {
if (this.state === "pending") {
this.callbackStack.push(() =>
this.handle(promise2, onFulfilled, onRejected, resolve, reject)
);
return;
}
let fn = this.state === "fulfilled" ? onFulfilled : onRejected;
let x;
try {
x = fn(this.value);
} catch (e) {
return reject(e);
}
this.resolvePromise(x, promise2, resolve, reject);
}
resolvePromise(x, promise2, resolve, reject) {
let self = this;
let called = false;
if (x === promise2) return reject(new TypeError("TypeError"));
try {
// 2.3.2
if (x instanceof Promise) {
x.then(resolve, reject);
return;
}
if (x !== null && (typeof x === "object" || typeof x === "function")) {
let then = x.then;
if (typeof then === "function") {
return then.call(
x,
function(v) {
if (called) return;
called = true;
//2.3.3.3.1
self.resolvePromise(v, promise2, resolve, reject);
},
function(y) {
if (called) return;
called = true;
reject(y);
}
);
}
}
resolve(x);
} catch (e) {
if (called) return;
called = true;
reject(e);
}
}
}
Promise.deferred = Promise.defer = function() {
let dfd = {};
dfd.promise = new Promise(function(resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};