PromiseA+規(guī)范下實現(xiàn)的Promise

該實現(xiàn)是按照promiseA+規(guī)范來進(jìn)行梳理的
當(dāng)使用promise的時候需要先new一個實例,所以我們要構(gòu)造一個promise構(gòu)造函數(shù)

let p = new Promise(function(resolve, reject){
    // 先調(diào)誰,就走誰
    reject('不對了');  // 失敗
    resolve(1);  // 成功  

    // 如果拋出異常也會走到then的失敗函數(shù)里
    throw new Error('錯了'); 
});
p.then(function(data) {
    console.log(data);    
}, function(err) {
    console.log(err);      // '不對了'
});

new的Promise實例包含一個執(zhí)行函數(shù)(executor),該函數(shù)有兩個參數(shù),分別是成功和失敗的方法
這里要記住一點是成功和失敗的方法是互斥的,同時調(diào)用時,先調(diào)用的先執(zhí)行,后面調(diào)用的不會再執(zhí)行

狀態(tài)

都知道promise是有三種狀態(tài)值的

  • pending(待定) 默認(rèn)的狀態(tài)
  • fulfilled(成功)
  • rejected(失敗)
    那么給構(gòu)造函數(shù)寫一個status,用來保存狀態(tài)的變化
    pendding可以轉(zhuǎn)為fulfilled或者rejected,一旦轉(zhuǎn)換了,status就不會改變了,要么是成功態(tài),要么是失敗態(tài)
// 實現(xiàn)promise
function Promise(executor) {
    const self = this;
    self.status = 'pending';    // 默認(rèn)的狀態(tài),pending->fulfilled, pending->rejected
    self.value = null;      // 成功的值
    self.reason = null;     // 失敗的原因

    function resolve(value) {   // 成功
        if (self.status === 'pending') {
            self.status = 'fulfilled';
            self.value = value;
        }
    }
    function reject(reason) {       // 失敗
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
        }
    }


    try {
        executor(resolve, reject);
    } catch(e) {        // 如果拋出錯誤,就直接走失敗的方法
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
    const self = this;
    if (self.status === 'fulfilled') {
        onFulfilled();
    }
    if (self.status === 'rejected') {
        onRejected();
    }
};

module.exports = Promise;

上面寫了很多,但是發(fā)現(xiàn)都是處理同步的情況,當(dāng)遇到異步的時候該怎么處理呢,我們繼續(xù)往下寫

異步promise

promise中可以寫異步代碼,并且可以進(jìn)行多次then

let p = new Promise(function(resolve, reject){
    // 異步操作
    setTimeout(function() {
        resolve('ok');
    }, 1000);
});
p.then(function(data) {
    console.log(data);    
}, function(err) {
    console.log(err);     
});
p.then(function(data) {
    console.log(data);    
}, function(err) {
    console.log(err);     
});

實現(xiàn)一下異步的promise
promise實例可以多次then,當(dāng)成功后會將then中的成功的方法依次執(zhí)行,我們可以先將then中成功的回調(diào)和失敗的回調(diào)存到數(shù)組內(nèi),當(dāng)成功時調(diào)用成功的數(shù)組即可

function Promise() {
    self.status = 'pending';
    self.value = null;
    self.reason = null;
+   self.onFulfilledCb = [];    // 存放then成功的回調(diào)
+   self.onRejectedCb = [];     // 存放then失敗的回調(diào)   

    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'fulfilled';
            self.value = value;
     +      self.onFulfilledCb.forEach(function (fn) {
                fn();
            });
        }
    }
    
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
      +     self.onRejectedCb.forEach(function (fn) {
                fn();
            });
        }
    }  

    try {
        executor(resolve, reject);
    } catch(e) {        // 如果拋出錯誤,就直接走失敗的方法
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
    const self = this;
    if (self.status === 'fulfilled') {
        onFulfilled(self.value);
    } 
    if (self.status === 'rejected') {
        onRejected(self.reason);
    }
    // 當(dāng)調(diào)用then時 可能沒成功也沒失敗,就處于pending狀態(tài)

+   if (self.status === 'pending') {
        // 將成功的回調(diào)添加到數(shù)組中
        self.onFulfilledCb.push(function () {
            onFulfilled(self.value);
        });
        self.onRejectedCb.push(function () {
            onRejected(self.reason);
        });
    }
};

module.exports = Promise;

鏈?zhǔn)秸{(diào)用

上面的代碼實現(xiàn)了異步調(diào)用的時候then方法是怎么處理的情況,
那么接下來就來到重要的環(huán)節(jié)了,大家都知道promise的then方法也是支持鏈?zhǔn)秸{(diào)用的
不過then的鏈?zhǔn)秸{(diào)用可不同于jquery返回this的方式實現(xiàn)的
?? 它是返回了一個新的promise
下面來看代碼

let p = new Promise(function(resolve, reject) {
        resolve('ok');
});
p.then(function (data) {
    console.log(data);
    return data;
}, function (err) {
    console.log(err);
    // return '有返回值就走成功態(tài)'
}).then(function (data) {
    console.log('2 '+data);
}, function (err) {
    console.log(err);
})

now讓我們開始實現(xiàn)鏈?zhǔn)秸{(diào)用,還是老樣子,+號代表實現(xiàn)的主要代碼:

Promise.prototype.then = function (onFulfilled, onRejected) {
    const self = this;
 +  let promise2;    // 定義一個promise2變量

    if (self.status === 'fulfilled') {
         // 返回一個新的promise
 +      promise2 = new Promise(function (resolve, reject) {
            // x可能是個普通值,也可能是個promise
 +          let x = onFulfilled(self.value);
            // x也可能是別人寫的promise,寫一個函數(shù)統(tǒng)一去處理
 +          resolvePromise(promise2, x, resolve, reject);
        });
    }
    if (self.status === 'rejected') {
 +       promise2 = new Promise(function (resolve, reject) {
 +          let x = onRejected(self.reason);
 +          resolvePromise(promise2, x, resolve, reject);
        });
    }
    // 當(dāng)調(diào)用then時 可能沒成功也沒失敗,就處于pending狀態(tài)
    if (self.status === 'pending') {
        // 將成功的回調(diào)添加到數(shù)組中
+        promise2 = new Promise(function (resolve, reject) {
            self.onFulfilledCb.push(function () {
+               let x = onFulfilled(self.value);
+               resolvePromise(promise2, x, resolve, reject);
            });

            self.onRejectedCb.push(function () {
+               let x = onRejected(self.reason);
+               resolvePromise(promise2, x, resolve, reject);
            });
        });
    }

    return promise2;
};
function resolvePromise(p2, x, resolve, reject) {
    if (p2 === x) {    // 不能返回自己
        return reject(new TypeError('循環(huán)引用'));
    }
    let called;     // 表示調(diào)用成功or失敗
    // x返回的可能是對象和函數(shù)也可能是一個普通的值
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then;

            if (typeof then === 'function') {
                then.call(x, function (y) {
                    // 防止多次調(diào)用
                    if (called) return;
                    called = true;
                    // y可能還是個promise,所以遞歸繼續(xù)解析只到返回一個普通值
                    resolvePromise(p2, y, resolve, reject);
                }, function (e) {
                    if (called) return;
                    called = true;
                    reject(e);
                });
            }
        } catch(e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x);     // 返回一個普通值
    }
}

通過以上代碼也可以得出了一些結(jié)論

  • 如果then中無論是成功的回調(diào)還是失敗的回調(diào)有return返回值,都會走下一個then的成功
  • 如果第一個promise返回了一個普通值,會進(jìn)入下一次then的成功回調(diào);如果第一個promise返回了一個新的promise,需要等待返回promise執(zhí)行的結(jié)果傳遞給下一次的then中
  • resolvePromise函數(shù),返回的結(jié)果和promise是同一個,既不會成功也不會失敗,我們就報一個類型錯誤提示一下
    以上我們已經(jīng)實現(xiàn)了promise的鏈?zhǔn)秸{(diào)用了,但這還不夠promise有一種情況是在then中什么都不傳的情況,還繼續(xù)鏈?zhǔn)秸{(diào)用
let p = new Promise(function (resolve, reject) {
    reject(999);
});
p.then().then().then(function (data) {
    console.log(data);  // 999    
}, function (err) {
    console.log(err);
});

這就是promise中值的穿透,該實現(xiàn)是在then中,那么我們也對then方法加以改良去實現(xiàn)一下

Promise.prototype.then = function(onFulfilled, onRejected) {
+    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value
    };
+    onRejected = typeof onRejected === 'function' ? onFulfilled : function (err) {
        throw err
    };  
    const self = this;
    let promise2;
};

其實很簡單,就是在then的時候如果你不給我傳回調(diào)函數(shù),那么我就判斷一下,確實沒有傳,那我就給你新創(chuàng)建一個函數(shù)然后在成功回調(diào)的時候把值傳到下一個then中去
另外在promise規(guī)范中要求,所有的onFulfilled和onRejected都需要異步執(zhí)行,所以我們要給他們加上異步,顯然這個更容易實現(xiàn)了,看下面代碼

Promise.prototype.then = function(onFulfilled, onRejected) {
    if (self.status === 'fulfilled') {
        promise2 = new Promise(function (resolve, reject) {
            // 加上setTimeout實現(xiàn)異步調(diào)用
+           setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
+           setTimeout(function () {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }
};
    if (self.status === 'pending') {
        // 將成功的回調(diào)添加到數(shù)組中
        promise2 = new Promise(function (resolve, reject) {
+           setTimeout(function () {
                try {
                    self.onFulfilledCb.push(function () {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    });
                } catch(e) {
                    reject(e);
                }
            });

+           setTimeout(function () {
                try {
                    self.onRejectedCb.push(function () {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    });
                } catch(e) {
                    reject(e);
                }
            });
        });
    }

好了,我們以上就實現(xiàn)了promise中最重要的then方法了,寫的不好請多理解吧。
不過既然把then都寫完了,那接下來再寫幾個其他的,寫多點也讓大家一起來研究研究。

捕獲錯誤的方法

先來看下用法,其實這個和then方法里失敗的回調(diào)onRejected是一樣的

let p = new Promise(function(resolve, reject) {
    reject('錯錯錯');
});
p.then(function(data){
    console.log(data);
}).catch(function(e){
    console.log(e);    // '錯錯錯'
});

下面上代碼:

// 捕獲錯誤的方法
Promise.prototype.catch = function(callback) {
     // 也是調(diào)用then方法,給成功的回調(diào)傳一個null,給失敗的回調(diào)傳入callback
    return this.then(null, callback); 
};

Promise類自身也有一些方法,常用的有all, race, resolve, reject和defer(deferred)
都寫到這里了,那就繼續(xù)寫下去,廢話不多說,開擼?。?!
Promise.all()的用法

let p = new Promise(function(resolve, reject) {
   setTimeout(function() {
        resolve('北京');
    }, 1000);
});
let p2 = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve('南京');
    }, 200);
});
let p3 = new Promise(function(resolve, reject) {
    resolve('東京');
});
Promise.all([p, p2, p3]).then(function(data) {
    // all方法是所有的promise都成功后才會成功
    // 按照傳入all里的順序依次執(zhí)行,p里面的定時器只是執(zhí)行完成的時間而已,不影響all里輸出順序
    // 如:p這個需要等1秒后才會返回成功態(tài),p2就需要等待1秒時間
    console.log(data);    // [ '北京', '南京', '東京' ]
});

上面知道了用法,下面就來寫實現(xiàn),客官且留步繼續(xù)看下去

Promise.all = function(items) {
    return new Promise(function(resolve, reject) {
        let res = [];    // 用來存儲成功函數(shù)返回的值
        let num = 0;  // 記錄都返回成功的數(shù)字
        let len = items.length;  // 數(shù)組的長度 
        // 對數(shù)組進(jìn)行遍歷
        for (let i = 0; i < len; i++) {
              items[i].then(function(data) {
                  res[i] = data;
                  if (++num === len) {
                      resolve(res);
                  }
              }, reject);
        }
    });
};

如此這般,這般如此,就實現(xiàn)了all
再接再厲還有race方法
那么race方法顧名思義,race就是比賽,就是在比誰快,誰先成功,返回的就是誰的成功數(shù)據(jù)
來看一下怎么用

let p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('北京');
    }, 1000);
});
let p2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('南京');
    }, 500);
});
let p3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('東京');
    });
});
Promise.race([p, p2, p3]).then(function (data) {
    // p3最先執(zhí)行完返回成功,所以data就為p3的值,其他的p,p2都不返回
    console.log(data);  // '東京'
});

那么,不啰嗦了,寫吧

Promise.race = function(items) {
    return new Promise(function(resolve, reject) {
        for (let i = 0; i < items.length; i++) {
            items[i].then(resolve, reject);
        }
    });
};

以上就實現(xiàn)了race方法,那么一鼓作氣再寫下resolve和reject吧
這兩個方法看起來就非常簡單,因為之前的鋪墊已經(jīng)很好了基本屬于直接用的狀態(tài)
還是先來看下用法吧

// 成功
Promise.resolve([1,2,3,4]).then(function(data) {
    console.log(data);  // [1,2,3,4]
});
// 失敗
Promise.reject('err!').then(function () {}, function (err) {
    console.log(err);   // 'err!'
});

來快點,開寫,不猶豫

Promise.resolve = function(value) {
    return new Promise(function(resolve, reject) {
        resolve(value);
    });
};
Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
        reject(reason);
    });
};

完成?。?!
各位客官,是不是覺得感覺身體被掏空了,寫了這么多終于完事了,可以來梳理一下了。錯了,還有最后一個方法沒寫,它就是defer
defer方法的作用是什么呢?首先它不需要寫new來生成promise實例了,其次是因為promise的測試庫promises-aplus-tests需要寫這么一個方法,哈哈哈
看下怎么用吧,其實和Q是一樣的

function read() {
    let defer = Promise.defer();  // Q里寫的是Q.defer()
    require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
        if(err) defer.reject(err);
        defer.resolve(data);
    })
    return defer.promise;
}
read().then(function (data) {
    console.log(data)
},function(err){
    console.log(err);
})

為了測試庫的需要,實現(xiàn)最后一個吧

Promise.defer = Promise.deferred = function() {
    let def = {};
    def.promise = new Promise(function(resolve, reject) {
        def.resolve = resolve;
        def.reject = reject;
    });

    return def;
}

終于,完成了所有的實現(xiàn)了。實屬不易,也謝謝大家堅持看完,希望有所收獲吧!哈哈

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容