根據Promise/A+規範模擬實現Promise

1 什麼是Promise

1.1 Promise 解決的問題

1.1.1 回調地獄

咱們在作項目的時候常常遇到多個回調函數嵌套在一塊兒的狀況,一層套一層,代碼不夠直觀而且不容易維護,就是所謂的回調地獄。Promise 就很好的解決了這個問題。npm

1.1.2 並行結果

若是幾個異步操做之間並無先後順序之分,但須要等多個異步操做都完成後才能執行後續的任務,沒法實現並行節約時間。數組

1.2 Promise的簡介

Promise是抽象異步處理對象以及對其進行各類操做的組件。在 ES6 Promises 標準中定義的API還不是不少。目前大體有下面三種類型。promise

1.2.1 Constructor

//要想建立一個promise對象、可使用new來調用Promise的構造器來進行實例化。
var promise = new Promise(function(resolve, reject) {
    // 異步處理
    // 處理結束後、調用resolve 或 reject
});
複製代碼

1.2.2 Instance Method

// 對經過new生成的promise對象爲了設置其值在成功/失敗時調用的回調函數 可使用promise.then() 實例方法。
// resolve(成功)時onFulfilled 會被調用
// reject(失敗)時onRejected 會被調用

promise.then(onFulfilled, onRejected)
複製代碼

1.2.3 Static Method

// 一些對Promise進行操做的輔助方法,包括 Promise.all() 、Promise.race() 、Promise.resolve()、Promise.reject() 等
複製代碼

2 Promise經常使用的API

2.1 Promise.all

  • 參數:接受一個數組,數組內都是Promise實例
  • 返回值:返回一個Promise實例,這個Promise實例的狀態轉移取決於參數的Promise實例的狀態變化。當參數中全部的實例都處於resolve狀態時,返回的Promise實例會變爲resolve狀態。若是參數中任意一個實例處於reject狀態,返回的Promise實例變爲reject狀態。 Promise.all([p1, p2]).then(function (result) { console.log(result); // [ '2.txt', '2' ] });

無論兩個promise誰先完成,Promise.all 方法會按照數組裏面的順序將結果返回異步

2.2 Promise.race

  • 參數:接受一個數組,數組內都是Promise實例
  • 返回值:返回一個Promise實例,這個Promise實例的狀態轉移取決於參數的Promise實例的狀態變化。當參數中任何一個實例處於resolve狀態時,返回的Promise實例會變爲resolve狀態。若是參數中任意一個實例處於reject狀態,返回的Promise實例變爲reject狀態。
Promise.race([p1, p2]).then(function (result) {
  console.log(result); // [ '2.txt', '2' ]
});
複製代碼

2.3 Promise.resolve

  • 返回一個Promise實例,這個實例處於resolve狀態。
  • 根據傳入的參數不一樣有不一樣的功能:值(對象、數組、字符串等) 會 做爲resolve傳遞出去的值; Promise實例:原封不動返回。

2.4 Promise.reject

  • 返回一個Promise實例,這個實例處於reject狀態。
  • 參數通常就是拋出的錯誤信息。

3 Promise源碼的實現

以前接觸過Promise,可是對Promise的調用和狀態變化一直很模糊,死記硬背後過了一段時間就忘記了,很痛苦,一直想要從根本上弄懂Promise。最近直接根據Promise/A+規範,本身實現了一個簡單版本的Promise庫。廢話很少說,上乾貨。函數

3.1 Promise的初始化

// Promise 有三種狀態(pending, fulfilled, rejected),初始值爲pending。
// Promise 有兩個執行函數來改變狀態的值,成功的時候執行resolve,失敗的時候執行reject。
複製代碼
//構造函數中
function Promise(executor) {
    let self = this;
    
    /*初始化status*/
    self.status = 'pending';
    /*初始化value*/
    self.value = undefined;
    /*訂閱事件的數組*/
    self.onResolvedCallBacks = [];
    self.onRejectedCallBacks = [];
    
    /*此函數將Promise實例的狀態由pending 轉化爲 fulfilled*/
    function resolve(value) {
        if (value instanceof Promise) {
            return value.then(resolve, reject);
        }
        setTimeout(function () {
            if (self.status === 'pending') {
                self.status = 'fulfilled';
                self.value = value;
                /*發佈已經訂閱過的事件*/
                self.onResolvedCallBacks.forEach(item => item(self.value))
            }
        }, 0)
    }
    /*此函數將Promise實例的狀態由pending 轉化爲 rejected*/
    function reject(reason) {
        setTimeout(function () {
            if (self.status === 'pending') {
                self.status = 'rejected';
                self.value = reason;
                /*發佈已經訂閱過的事件*/
                self.onRejectedCallBacks.forEach(item => item(self.value))
            }
        }, 0)
    }
    
    // new Promise 的時候,執行器(executor)的代碼會當即執行
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
    
}
複製代碼

3.2 Promise的then方法的實現和Promise的鏈式調用

每次調用then方法後都會返回一個新的Promise實例測試

Promise.prototype.then = function (onFulfilled, onRejected) {
    /*當沒有函數傳遞進來的時候,添加默認函數*/
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value
    };
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {
        throw err
    };
    
    let self = this;
    /*因爲要實現鏈式調用,因此每次執行then方法的時候都會返回一個新的Promise實例*/
    let promise2;
    if (self.status === 'fulfilled') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    /*將onFulfilled函數執行的結果resolve掉*/
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e);
                }
            }, 0)
        })
    }
    
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    /*將onRejected函數執行的結果reject掉*/
                    let x = onRejected(self.value);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e);
                }
            }, 0)
        })
    }
    
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            /*訂閱事件*/
            self.onResolvedCallBacks.push(function () {
                try {
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e);
                }
            });
            
            self.onRejectedCallBacks.push(function () {
                try {
                    let x = onRejected(self.value);
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e);
                }
            });
        })
    }
    return promise2;
};

/*輔助函數 --> 解決多層嵌套狀況*/
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('循環引用'))
    }
    let then, called;
    if (x != null && (typeof x === 'function' || typeof x === 'object')) {
        try {
            then = x.then;
            if (typeof then === 'function') {
                then.call(x, function (data) {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, data, resolve, reject)
                }, function (err) {
                    if (called) return;
                    called = true;
                    reject(err);
                })
            } else {
                resolve(x);
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}
複製代碼

3.3 Promise.catch的實現

<!--其實就是then的變形-->
Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
};

/* 測試Promise/A+規範的方法 npm i -g promises-aplus-tests promises-aplus-tests Promise.js */
複製代碼

4 本身實現Promise的幾種經常使用方法

4.1 Promise.all

Promise.all = function (promises) {
    return new Promise(function (resolve, reject) {
        let count = 0;
        let result = [];
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (data) {
                result[i] = data;
                if (++count === promises.length) {
                    resolve(result);
                }
            }, function (err) {
                reject(err);
            });
        }
    })
};
複製代碼

4.2 Promise.race

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

4.3 Promise.resolve

Promise.resolve = function (value) {
    return new Promise(function (resolve, reject) {
        resolve(value);
    })
};
複製代碼

4.4 Promise.reject

Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
        reject(reason);
    })
};
複製代碼

5 本身的感想

費了九牛二虎之力,終於勉強實現了Promise的功能。給我最直接的感受就是,看似難懂的東西只要弄懂了後面是怎麼實現的,用法就很簡單了。固然在弄懂源碼邏輯道路確實不容易,可是一遍不行兩遍,兩遍不行三遍。。。依次次類推,每次都有不一樣的收穫,我想這就是傳說中的‘讀書百遍,其義自見’吧。ui

相關文章
相關標籤/搜索