JavaScript——promise/a+规范实现

125 阅读1分钟
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const PENDING = "pending";

function Promise(executor) {
    let self = this;
    self.status = PENDING;
    self.value = undefined;
    self.reason = undefined;
    self.onFulfilledCallbacks = [];
    self.onRejectedCallbacks = [];
    
    // setTimeout 原因 : 
    // 规范规定 onFulfilled 和 onRejected 不能被调用
    // 直到执行栈只包含平台代码
    // 平台代码指 引擎, 环境, promise 实现代码
    
    function resolve(value) {
        setTimeout(() => {
            self.status = FULFILLED;
            self.value = value;
            self.onFulfilledCallbacks.forEach(cb => cb(value));
        });
    }
    
    function reject(reason) {
        setTimeout(() => {
            self.status = REJECTED;
            self.reason = reason;
            self.onRejectedCallbacks.forEach(cb => cb(reason));
        })
    }
    
    try {
        executor(resolve, reject);
    } catch(e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this, promise2;
    // 三元表达式解决值穿透的问题
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason; };
    
    if (self.status === FULFILLED) {
        return promise2 = new Promise((res, rej) => {
            setTimeout(() => {
                try {
                    let x = onFulfilled(self.value);
                    __resolve__(promise2, x, res, rej);
                } catch (e) {
                    rej(e);
                }
            });
        });
    }
    
    if (self.status === REJECTED) {
        return promise2 = new Promise((res, rej) => {
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    __resolve__(promise2, x, res, rej);
                } catch (e) {
                    rej(e);
                }
            });
        });
    }
    
    if (self.status === PENDING) {
        return promise2 = new Promise((res, rej) => {
            self.onFulfilledCallbacks.push(value => {
                try {
                    let x = onFulfilled(value);
                    __resolve__(promise2, x, res, rej);
                } catch (e) {
                    rej(e);
                }
            });
            
            self.onRejectedCallbacks.push(reason => {
                try {
                    let x = onRejected(reason);
                    __resolve__(promise2, ex, res, rej);
                } catch (e) {
                    rej(e);
                }
            });
        });
    }
}

function __resolve__(promise2, x, res, rej) {
    if (promise2 === x) throw TypeError("循环引用");
    
    let called = false;
    if (x !== null && (typeof x === "object" || typeof x === "function")) {
        try {
            let then = x.then;
            if (typeof then === "function") {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    __resolve__(promise2, y, res, rej);
                }, reason => {
                    if (called) return;
                    called = true;
                    rej(reason);
                });
            }
            else res(x);
        } catch (e) {
            rej(e);
        }
    }
    else res(x);
}

Promise.prototype.catch = function(onRejected) {
    return this.then(undefined, onRejected);
}

Promise.all = function (promises) {
    let rejected = false, cnt = promises.length, values = [];
    return new Promise((res, rej) => {
        promises.forEach(p => {
            p.then(value => {
                if (rejected) return;
                values.push(value);
                if (--cnt === 0) res(values);
            }, reason => {
                if (rejected) return;
                rejected = true;
                rej(reason);
            })
        })
    });
}

Promise.resolve = function(value) {
  return new MyPromise(res => res(value));
}

Promise.reject = function(reason) {
  return new MyPromise((res, rej) => rej(reason));
}