簡易版promise源碼實現

 首先咱們先看一下promise是如何使用的:javascript

<script>
            let p1=new Promise((resolve,reject)=>{
                    resolve()
            })
            p1.then(()=>{
                console.log(123)
            })
 </script>

  經過promise構建出來的對象有三種狀態,Pending(進行中),Fulfilled(已成功),Rejected(已失敗)java

狀態只能由 Pending 變爲 Fulfilled 或由 Pending 變爲 Rejected ,且狀態改變以後不會在發生變化,會一直保持這個狀態。

  經過then函數註冊成功或者失敗函數,promise內部調用resolve或者reject調用對應的函數。jquery

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            if (typeof func != "function") {
                throw TypeError("Promise resolver " + func + " is not a function")
            }
            func(this._resolve.bind(this), this._reject.bind(this));
        }
        _resolve(val) {
        }
        _reject(val) {
        }
    }
    window.customePromise = customePromise;
})();

 首先咱們定義了一個customePromise的類,經過window暴露出去,經過前面promise調用咱們能夠看到參數是一個函數,該函數接受兩個參數,resolve,reject,經過調用兩個函數來執行對應的成功或者失敗函數。第一步,咱們在內部首先要判斷傳入是否爲一個函數,才進行下一步操做。數組

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            this._status = status[0];
            this.resolveArr = [];
            this.rejectArr = [];
            this.value="";
            if(typeof func!="function"){
                throw TypeError("Promise resolver "+ func+" is not a function")
            }
            try {
                func(this._resolve.bind(this), this._reject.bind(this));
            }
            catch (err) {
                this._reject(err)
            }
        }
        _resolve(val) {
        }
        _reject(val) {
        }
    }
    window.customePromise = customePromise;
})();

promise最大的好處就是他的捕獲機制,一旦你在外部函數吃錯均可以在內部能夠捕獲到,這樣你外面的腳本還能繼續往下執行,並經過失敗函數能夠打印出來。promise

一開始咱們在構造函數開始執行的時候保存了這個對象狀態爲pending,而後在這個promise對象上掛載了兩個屬性用來保存成功和失敗函數。下面咱們來實現then函數異步

then(resolveFunc, rejectFunc) {
            switch (this._status) {
                case "pending":
                    this.resolveArr.push(resolveFunc);
                    this.rejectArr.push(rejectFunc);
                    break;
                case "fulfilled":
                    resolvefunc(this.value)
                    break;
                case "rejected":
                    rejectfunc(this.value)
                    break;
            }
}

經過判斷promise對象來決定函數是否執行,若是處於pending狀態,就把成功失敗函數存入對應的數組,若是狀態已經改變,就執行對應的函數。下面實現成功失敗函數的代碼。函數

_resolve(val) {
            setTimeout(() => {
                this.value=val;
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                this.value=val;
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
 }

 注意下咱們的promise屬於微任務,他會讓咱們主線程的任務先執行,因此咱們這裏採用異步模擬。但這裏仍是存在一個問題,就是咱們能夠在外部屢次調用,這明顯是不容許,因此咱們來優化下代碼。優化

_resolve(val) {
            setTimeout(() => {
                if (this._status != status[0]) {
                    return;
                }
                this.value=val;
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (this._status != status[0]) {
                    return;
                }
                this.value=val;
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
}

 用過jquery的同窗都知道jquery是能夠鏈式調用,一樣咱們的promise也是能夠的,那就表示咱們的then函數返回值一樣也是個promise對象,下面咱們來改寫then函數。this

then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    let value = resolveFunc(val);
                    resolve(value)
                }
                rejectfunc = function (val) {
                    let value = rejectFunc(val);
                    reject(value)
                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })

        }

  注意這裏並很差理解,由於以前咱們存入的事成功和失敗函數,這裏咱們包裝了一個函數並把then函數返回值promise對象的resolve函數也存入進去,這就表示若是咱們第一個promise狀態改變會接着觸發第二個promise對象的執行,但須要注意若是咱們函數的返回值一樣是一個promise對象的話,咱們必要要等待其狀態改變才能觸發他的下一個的then函數,這是一個難點,很很差理解,咱們使用過把then函數返回的promise對象的resolve時間掛在函數內部的then上面,一旦內部函數狀態改變會觸發then函數的對應的事件,固然咱們也是要加上容錯處理,下面附上代碼: 線程

then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    try {
                        if (typeof resolveFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = resolveFunc(val);
                            if (value instanceof customePromise) {
                                value.then(resolve)
                            }
                            else {
                                resolve(value)
                            }
                        }
                    }
                    catch (err) {
                        console.log(err)
                        reject(err)
                    }
                }
                rejectfunc = function (val) {
                    try {
                        if (typeof rejectFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = rejectFunc(val);
                            if (value instanceof customePromise) {
                                value.then(reject)
                            }
                            else {
                                reject(value)
                            }
                        }
                    }
                    catch (err) {
                        reject(err)
                    }

                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })
        }

 最後說一下resolve()參數若是是一個promise對象的話,必需要等待參數的狀態改變才能觸發他的then函數,原理跟上面的很類似。

_resolve(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[1];
                        this.resolveArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[1];
                this.resolveArr.forEach(item => {

                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[2];
                        this.rejectArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }

最後附上全部的代碼:

; (function () {
    const status = {
        0: "pending",
        1: "fulfilled",
        2: "rejected"
    }
    class customePromise {
        constructor(func) {
            this._status = status[0];
            this.resolveArr = [];
            this.rejectArr = [];
            if(typeof func!="function"){
                throw TypeError("Promise resolver "+ func+" is not a function")
            }
            try {
                func(this._resolve.bind(this), this._reject.bind(this));
            }
            catch (err) {
                this._reject(err)
            }
        }
        _resolve(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[1];
                        this.resolveArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[1];
                this.resolveArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        _reject(val) {
            setTimeout(() => {
                if (val instanceof customePromise) {
                    val.then(()=>{
                        this._status = status[2];
                        this.rejectArr.forEach(item => {
                            item(val);
                        })
                    })
                    return;
                }
                if (this._status != status[0]) {
                    return;
                }
                this._status = status[2];
                this.rejectArr.forEach(item => {
                    item(val);
                })
            }, 0)
        }
        then(resolveFunc, rejectFunc) {
            let resolvefunc, rejectfunc;
            let _this = this;
            return new customePromise(function (resolve, reject) {
                resolvefunc = function (val) {
                    try {
                        if (typeof resolveFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = resolveFunc(val);
                            if (value instanceof customePromise) {
                                value.then(resolve)
                            }
                            else {
                                resolve(value)
                            }
                        }
                    }
                    catch (err) {
                        console.log(err)
                        reject(err)
                    }

                }
                rejectfunc = function (val) {
                    try {
                        if (typeof rejectFunc != "function") {
                            resolve(val)
                        }
                        else {
                            let value = rejectFunc(val);
                            if (value instanceof customePromise) {
                                value.then(reject)
                            }
                            else {
                                reject(value)
                            }
                        }
                    }
                    catch (err) {
                        reject(err)
                    }
                }
                switch (_this._status) {
                    case "pending":
                        _this.resolveArr.push(resolvefunc);
                        _this.rejectArr.push(rejectfunc);
                        break;
                    case "fulfilled":
                        resolvefunc()
                        break;
                    case "rejected":
                        rejectfunc
                        break;
                }
            })
        }
    }
    window.customePromise = customePromise;
})();
相關文章
相關標籤/搜索