whu12yz / tech_blog

一些技术总结和源码解读
1 stars 0 forks source link

ok实现一个promise #1

Open whu12yz opened 5 years ago

whu12yz commented 5 years ago
function isFunc(func) {
    return Object.prototype.toString.call(func) === '[object Function]';
}
function pureFunc(value) {
    return value;
}
function pureErrorFunc(err) {
    throw err;
}

function isNode() {
    return typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
}

function schedule(callback) {
    if (!isFunc(callback)) return;

    // 确保task 异步在microtask中执行
    if(isNode()) {
        return process.nextTick(callback);
    } else if(typeof MutationObserver !== 'undefined') {
        const div = document.createElement('div');
        const observer = new MutationObserver(() => {
            callback();
            observer.disconnect();
        })
        observer.observe(div, {attributes: true});
        div.classList.toggle('trigger')
    } else if (typeof setImmediate !== 'undefined') {
        setImmediate(calback);
    } else if (typeof setTimeout !== 'undefined') {
        setTimeout(callback);
    }

}

function resolvePromise(onResolved, value, resolve, reject) {
    schedule(() => {
        try{
            const result = onResolved(value);
            if (result instanceof Promise) {
                result.then(resolve, reject);
            } else {
                resolve(result);
            }
        }catch(err) {
            reject(err);
        }
    })
}

function rejectPromise(onRejected, value, resolve, reject) {
    schedule(() => {
        try{
            const result = onRejected(value);
            if (result instanceof Promise) {
                result.then(resolve, reject);
            } else {
                resolve(result);
            }
        }catch(err) {
            reject(err);
        }
    })
}

class Promise {
    constructor(excutor) {
        if (!isFunc(excutor)) {
            throw Error('new Promise param must be a function');
        }
        this.status = 'PENDING';
        this.data = ''
        this.resolveCallbackList = []; //promise结束之前可能会有多可回调添加上去
        this.rejectCallbackList = []; //同理
        const resolve = (value) => {
            if (this.status === 'PENDING') {
                this.status = 'RESOLVED';
                this.data = value;
                this.resolveCallbackList.forEach((onResolve) => {
                    schedule(() => {
                        onResolve(this.data);
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === 'PENDING') {
                this.status = 'REJECTED';
                this.data = reason;
                this.rejectCallbackList.forEach((onReject) => {
                    schedule(() => {
                        onReject(this.data);
                    })
                })
            }
        }
        try{
            excutor(resolve, reject);
        }catch(err) {
            reject(err);
        }
    }

    static resolve(value) {
        return new Promise((resolve, reject) => {
            resolve(value);
        });
    }

    static reject(err) {
        return new Promise((resolve, reject) => {
            reject(err);
        });
    }

    then (onResolved, onRejected) {
        onResolved = isFunc(onResolved) ? onResolved : pureFunc;
        onRejected = isFunc(onRejected) ? onRejected : pureErrorFunc;
        if (this.status === 'PENDING') {
            return new Promise((resolve, reject) => {
                this.resolveCallbackList.push((value) => {
                    resolvePromise(onResolved, value, resolve, reject);
                });
                this.rejectCallbackList.push((value) => {
                    rejectPromise(onRejected, value, resolve, reject);
                })
            })

        }

        if ( this.status === 'RESOLVED') {
            return new Promise((resolve, reject) => {
                resolvePromise(onResolved, this.data, resolve, reject);
            })
        }

        if ( this.status === 'REJECTED') {
            return new Promise((resolve, reject) => {
                rejectPromise(onRejected, this.data, resolve, reject);
            })

        }
    }

    catch(onRejected) {
        return this.then(null, onRejected);
    }

}

module.exports = Promise;
whu12yz commented 5 years ago

第一个版本用了Object.observe去实现异步,在microTask中执行。大部分浏览器都已经废弃此方法,现已在所有降级方案中去掉Object.observe.