promise

const PENDING = 'PENDING'; // 等待
const FULFILLED = 'FULFILLED'; // 成功
const REJECTED = 'REJECTED'; // 失败

// 该方法核心就是判断x
const resolvePromise = (promise2, x, resolve, reject) => {
    // 注意一个问题:promise2未定义;因为JS执行机制,new Promise没有执行完就传promise2 肯定是没有定义
    // 此时我们需要异步来解决,先让new Promise执行完;然后在调resolvePromise方法将promise2传入,这样就可以获取的到了

    // 判断x和promise2是不是同一个人,是则就报错
    if(promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
    let called; // 内部测试的时候  会成功和失败都调用
    //判断数据类型 typeof instanceof constructor toString
    if(x !== null && (typeof x === 'object' || typeof x === 'function')) {
        
        try {
            let then = x.then; // 取then 有可能这个then属性是通过 defineProperty定义
            if(typeof then === 'function') { // 当前有then方法,就姑且它是一个promise
                then.call(x, y => { // y 可能还是一个promise,递归调用,直到解析为一个普通值为止
                    if (called) {
                        return
                    }
                    called = true;
                    resolvePromise(promise2, y, resolve, reject); // 采用成功的结果向下传递
                }, r => {
                    if(called) {
                        return
                    }
                    called = true; // 防止多次调用成功和失败
                    reject(r); // 采用失败的结果向下传递
                }); // 为什么不直接用x.then;是为了保证不用再次取then的值;因为取该值有可能取不到
            } else {
                // {then: 1}
                resolve(x); // 说明x 是一个普通对象,直接成功即可
            }
        } catch (error) {
            if(called) {
                return
            }
            called = true;
            reject(error);
        }
    } else {
        // x 不是对象或函数 肯定是一个普通值
        resolve(x); // 直接让promise2成功
    }
}

class Promise {
    constructor(executor) {
        //不能相信用户的输入,一定要进行参数校验
        if (typeof executor !== 'function') {
            throw new TypeError(` Promise resolver ${executor} is not a function `);
        }
        this.status = PENDING;
        
        this.value = undefined; // 成功的值
        this.reason = undefined; // 失败的原因

        this.onResolvedCallbacks = []; // 成功的回调的数组
        this.onRejectedCallbacks = []; // 失败的回调的数组
        // 成功函数
        let resolve = (value) => {
            // 屏蔽调用;防止调resolve后又调reject
            if(this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn()); // 发布
            }
        }

        // 失败函数
        let reject = (reason) => {
            if(this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }

        try {
            executor(resolve, reject); // 默认执行器会默认执行
        } catch (error) {
            reject(error); // 若执行时发生了错误。等价于调用了失败态
        }
    }

    then(onfulfilled, onrejected) { // then 目前有两个参数
        // onfulfilled, onrejected是可选参数
        onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : val => val;
        
        onrejected = typeof onrejected === 'function' ? onrejected : err => {
            throw err;
        }


        let promise2 = new Promise((resolve, reject) => {
            // 同步情况
            if(this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onfulfilled(this.value);
                        // x可能是普通值  也可以是promise
                        // 判断x的值 ===》 promise2的状态 看是成功还是失败
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error); // 抛错的话直接调用promise2的reject方法
                    }
                }, 0);
            }

            if(this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }

            // 异步情况
            if(this.status === PENDING) {
                // 如果是异步就先订阅好
                this.onResolvedCallbacks.push(() => {
                    // todo...
                    setTimeout(() => {
                       try {
                           let x = onfulfilled(this.value);
                           resolvePromise(promise2, x, resolve, reject);
                       } catch (error) {
                           reject(error);
                       }
                    }, 0);
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                // 注意:加了异步setTimeout后;若代码里直接throw new Error抛错的话,上面的异常处理捕捉不到,因为是同步的,我们加了setTimeout后该部分就变成异步的了
                // 所以我们需要在处理异步的情况时候也要加个异常处理try catch
            }
        });
        
        return promise2;
    }
}

// 延迟对象
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

module.exports = Promise;

// 问题:1、为什么要加定时器?2、为什么在处理异步情况的时候也要加try-catch