promise v2.0版本

Tra發表於2018-12-29

實現一個複雜版本的promise

function PromiseA(executor){
    this.status = 'pending'
    // 成功value
    this.value = undefined;
    // 失敗reason
    this.reason = undefined;

    // 成功監聽陣列
    this.succArr = [];
    // 失敗監聽陣列
    this.errArr = [];

    var that = this;

    function resolve(value){
        if(that.status === 'pending'){
            that.status = 'resolved';
            that.value = value;
            that.succArr.forEach(function (item) {
                item(that.value)
            })
        }
    }

    function reject(reason) {
        if(that.status === 'pending'){
            that.status = 'rejected';
            that.reason = reason;
            that.errArr.forEach(function (item) {
                item(that.reason)
            })
        }

    }

    try {
        executor(resolve,reject)
    }catch (e) {
        reject(e)
    }

}

PromiseA.prototype = {
    constructor:PromiseA,
    then:function (succFn,errFn) {
        var that = this;
        var Promise2;
        // catch函式的核心
        errFn = typeof errFn === 'function' ?  errFn : function(err){ throw err}
        Promise2 = new PromiseA(function(resolve,reject){
            if(that.status === 'pending'){
                that.succArr.push(function () {
                    // 解決非同步問題,其實promise屬於微任務,這裡用巨集任務模擬非同步
                    setTimeout(function () {
                        try{
                            // then函式的返回值會包裝成新的promise
                            var x = succFn(that.value);
                            that.resolvePromise(Promise2, x, resolve, reject);

                        }catch (e) {
                            reject(e);

                        }
                    },0)


                });
                that.errArr.push(function () {
                    setTimeout(function () {
                        try{
                            var x =  errFn(that.reason);
                            that.resolvePromise(Promise2, x, resolve, reject);

                        }catch (e) {
                            reject(e);
                        }
                    },0)


                })
            }
            /*
               * 為什麼還要對status === 'resolved''rejected'做相關處理呢?
               * 原因:如果promise內部是非同步函式,不做相關處理也是可以的,但是對於內部是同步程式碼時,當執行resolve時,sussArr還未push到then內部函式,導致無法執行then內部函式
               *
               * */
            if(that.status === 'resolved'){
                setTimeout(function () {
                    try{
                        // then函式的返回值會包裝成新的promise
                        var x = succFn(that.value);
                        that.resolvePromise(Promise2, x, resolve, reject);


                    }catch (e) {
                        reject(e);

                    }

                },0)

            }
            if(that.status === 'rejected'){
                setTimeout(function () {
                    try{
                        var x =  errFn(that.reason);
                        that.resolvePromise(Promise2, x, resolve, reject);

                    }catch (e) {
                        reject(e);
                    }
                },0)


            }
        });


        return Promise2;
    },
    /*
    * @desc:resolvePromise主要解決 then內部  return { thenfunction(){}}  return function(){} ruturn new Promise情況
    * @這是Promise規範中的方法,瞭解流程即可
    * 其終極目的: resolve(data)
    *
    * 這個函式比較難理解
    * */
    resolvePromise: function(promise2, x, resolve, reject) {
        let self = this;
        let called = false;   // called 防止多次呼叫

        if (promise2 === x) {
            return reject(new TypeError('迴圈引用'));
        }

        if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
            // x是物件或者函式
            try {
                let then = x.then;

                if (typeof then === 'function') {
                    /*
                    * 難點:當返回是個promise時,但是確保其resolve reject要傳遞到我們寫的下一個then
                    *
                    * 1.當第一次呼叫then時,其實已經生成了一個新的promiseX
                    * 2.then內部函式又返回一個promiseY,但是在呼叫下一個then時,我們使用的時promiseX,那如何保證promiseY的resolve值傳遞到promiseX呢?
                    * 3.除非我們可以用promiseX的resolve(promiseY的resolve的值)
                    * 4.解決方法,內部呼叫then方法,將promiseY的resolve的值傳遞下來
                    * 5.遞迴處理
                    * 6.牢記我們這個函式的目的: promiseX的resolve(data)
                    * */
                    then.call(x, (y) => {
                        // 別人的Promise的then方法可能設定了getter等,使用called防止多次呼叫then方法
                        if (called) return ;
                        called = true;
                        // 成功值y有可能還是promise或者是具有then方法等,再次resolvePromise,直到成功值為基本型別或者非thenable
                        self.resolvePromise(promise2, y, resolve, reject);
                    }, (reason) => {
                        if (called) return ;
                        called = true;
                        reject(reason);
                    });
                } else {
                    if (called) return ;
                    called = true;
                    resolve(x);
                }
            } catch (reason) {
                if (called) return ;
                called = true;
                reject(reason);
            }
        } else {
            // x是普通值,直接resolve
            resolve(x);
        }
    },

    //實現catch函式:promise鏈中所有錯誤都會捕獲
    catch:function (errFn) {
        // 其實只要給then內部的errFn新增一個預設函式即可,因為每次then都會向下傳遞,所有catch寫到後面即可捕獲到錯誤
        return this.then(null,errFn)
    }

}複製程式碼


相關文章