手寫Promise原理


我的promise能實現什么?

1:解決回調地獄,實現異步

2:可以鏈式調用,可以嵌套調用

3:有等待態到成功態的方法,有等待態到失敗態的方法

4:可以衍生出周邊的方法,如Promise.resolve(),Promise.reject(),Promise.prototype.then(),Promise.prototype.catch(),Promise.all() // 所有的完成

5. 可以根據自己的需求調節自己的promise

let PromiseA = require('./PromiseA');

const promiseA = new PromiseA((resolve, reject) => {
    resolve(new PromiseA((resolve,reject)=>{
        setTimeout(()=>{
            resolve(100)
        },1000)
    }))
})


promiseA.then(data=>{
    console.log(data)
})

下面開始實現promise,首先創造三個常量,等待,成功,失敗

const PENDING = 'PENDING';   // 等待狀態
const RESOLVED = 'RESOLVED';   // 成功狀態
const REJECTED = 'REJECTED';   // 失敗狀態

然后創造一個promiseA類,里面有constructor,then方法,catch方法。這里的catch其實就是失敗的then,即then(null,(err)=>{...})


class PromiseA { constructor(){...} then(){...} 
    catch(err){
        return this.then(null,err)
    }
}

我們重點關注constructor和then,先來看constructor。這里promiseA默認的狀態是等待態,成功的值value默認為undefined,失敗的值reason默認為undefined。這里的onResolvedCallbacks和onRejectedCallbacks是一個發布訂閱的數組,我們先不管。然后有resolve方法,reject方法。如果傳進來是resolve,則改變value,並且讓狀態改為resolved。resolve里面可能還要promise,如果是的話,則執行它的then方法。reject方法同理,也是改變狀態和值。

還有Promise自帶一個executor執行器,就是傳進來的參數。會立即執行 。 但有可能出錯。所以用try,catch包住。 executor里有倆個參數,就是resolve和reject。也就是promise傳進來參數的倆個resolve,reject方法(constructor里的resolve和reject)

    constructor(executor) {
        this.status = PENDING; // 默認等待狀態
        this.value = undefined;    
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {                            
            if(value instanceof PromiseA){             
                value.then(resolve,reject)
                return
            }
            if (this.status === PENDING) {
                this.value = value;
                this.status = RESOLVED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e)
        }
    }

然后我們在看看then方法,then方法傳進來倆個參數onFulfilled,onRejected。這是倆個方法。一個成功的回調函數,一個失敗的回調函數。我們重點看一下,三個狀態的執行,即status的走向。

如果是resolve,即執行成功的回調onFulfilled。如果是reject,即執行失敗的回調onRejected。如果是等待,即執行一個發布訂閱的模式。發布訂閱 ( 其實就是,我先將成功的回調函數或者失敗的回調函數各自放入對應的數組,即是上面我們跳過的倆個數組onResolvedCallbacks和onRejectedCallbacks 。然后,當狀態改變為resolve或者reject的時候,即遍歷onResolvedCallbacks或者onRejectedCallbacks執行對應的回調函數 。至此異步就實現了,這個異步解決就是靠發布訂閱模式來解決的。)

三個狀態有三種走向,成功,失敗,等待。無論是哪種狀態,最終都會執行成功的回調函數onFulfilled或者失敗的回調函數onRejected。至此promise的基礎功能就實現了。

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
        onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
        let promise2 = new PromiseA((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(err);
                        }
                    });

                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(err);
                        }
                    });
                })
            }
        })
        return promise2;
    }

 

接下來我們繼續實現鏈式調用,鏈式調用的方法就是創建一個新的promise2對象,然后得到之前的promise的then方法的值。是怎么得到之前的then方法里面的值呢?就是上面的promise2和resolvePromise方法。我們接着往下看。

let promise2 = promiseA.then(e=>{
    return e
}
)
promise2.then(data=>{
    console.log(data,'123')
})

那么繼續實現,還是上面的函數。這里我們重點觀察這個promise2和resolvePromise. 我們先說promise2,這里的promise2,其實是一個新的promise.也就是說promise的鏈式調用靠的就是返回一個新的promise.這里把之前的三種狀態包起來,目的就是可以讓里面得到的結果,執行PromiseA的resolve方法和reject方法。有人可能會說,那么PromiseA的resolve或者reject要還是promise怎么辦?這里我們就要用到resolvePromise方法來判斷了。所以我們將成功的回調函數resolve換成resolvePromise方法來執行。這里我們要明白,resolve是執行成功的回調函數。不管狀態是成功還是失敗,如果執行成功都是走向resolve。所以resolve和reject的狀態如果執行成功都是走向resolve。

        let promise2 = new PromiseA((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(err);
                        }
                    });

                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(err);
                        }
                    });
                })
            }
        })
        return promise2;

然后我們在看resolvePromise方法,走到這里,說明是執行成功的回調函數了。傳進去的參數有promise2,x,promise2的resolve,promise2的reject。首先promise2等於一個構造函數PromiseA。 這樣傳進去參數是會報錯的。因為執行到這一步,promise2還沒有生成。所以是會報錯的。

所以我們加一個setTimeout包住它,這樣就可以等promise2生成完在執行。上面代碼的setTimeout涉及到了事件循環,也就是宏任務和微任務的部分。js執行機制是先執行主線程,然后執行微任務隊列,然后進行渲染,然后在執行宏任務隊列。宏任務執行完,如果宏任務里還包着js任務,就繼續循環反復。直到所有任務執行完成。這里的setTimeout是宏任務,promise2執行就是主線程,主線程在宏任務之前執行。

 resolvePromise(promise2, x, resolve, reject);

 

剛剛說到setTimeout,這里貼上一段代碼。這里的newPromise是在setTimeout前執行的。

console.log(1);

setTimeout(() => {
  console.log("我是定時器,延遲0S執行的");
}, 0);

new Promise((resolve, reject) => {
  console.log("new Promise是同步任務里面的宏任務");
  resolve("我是then里面的參數,promise里面的then方法是宏任務里面的微任務");
}).then(data => {
  console.log(data);
});

console.log(2);

 

好的參數都傳進去了,接下來我們看resolvePromise的具體方法。就是一個判斷回調函數x是不是promise,如果是就在循環拆開。直到不是為止。如果是普通值的話就可以直接返回了。至此,所以的promise庫就實現完了。至於后面的all和其他周邊方法就是語法糖了。主要核心部分掌握了,后面的周邊方法就不算什么了。

function resolvePromise(promise2, x, resolve, reject) {

    if (promise2 === x) {
        return reject(new TypeError('返回的promise和當前promise不能是同一個對象哦,會嵌入死循環'))
    }
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x,y=> {
                    resolvePromise(promise2, y, resolve, reject)
                },r=> {
                    reject(r)
                }
                )
            } else {
                resolve(x)
            }
        } catch (err) {
            reject(err)
        }
    } else {
        resolve(x);
    }
}

 至於constructor里的resolve方法里判斷,我們來看看。其實也是一個遞歸。判斷PromiseA里的resolve里面是不是promise,一直拆開。跟上面的方法類似。

        let resolve = (value) => {
            if(value instanceof PromiseA){
                value.then(resolve,reject)
                return
            }
            if (this.status === PENDING) {
                this.value = value;
                this.status = RESOLVED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }

而至於這倆行代碼,就是解決一個不斷向jquery那樣then的情況。如果是函數的話,將自己的參數作為結果返回。傳遞給下一個then。

        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
        onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
promiseA.then().then().then().then().then()

 

 

最后,如果有什么覺得奇怪的地方,歡迎互相討論,學習。謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM