Promise原理分析及源碼實現

為什么用Promise?

  1. Promise能解決回調地獄問題
  2. Promise能多個異步操作并行炮叶,獲取最終結果

Promise三種狀態(tài)

  • Pending Promise對象實例創(chuàng)建時候的初始狀態(tài)
  • Fulfilled 可以理解為成功的狀態(tài)
  • Rejected 可以理解為失敗的狀態(tài)

Promise狀態(tài)轉換

image

Promise一旦從pending狀態(tài)到resolve狀態(tài)或者reject狀態(tài)就不會再改變哥遮,能保證結果的正確性笨使,不會出現從成功到失敗或者從失敗到成功的情況镀裤。

原生Promise使用分析

let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
  },e=>{
    console.log("fail",e);
  })

Promise

Promise

創(chuàng)建一個Promise需要傳遞一個executor回調函數作為參數,來初始化Promise缴饭。executor中有兩個參數暑劝,一個resolve用來回調返回值或者另一個Promise,一個reject用來回調失敗值或者Error颗搂。

then()

then函數

then函數需傳遞兩個函數作為參數担猛,一個回調resolve的結果,一個回調reject的結果

const STATUS = {
    "PENDING": "pending",
    "FULFILLED": "fulfilled",
    "REJECTED": "rejected"
}


class Promise {

    /**
     *  Creates a new Promise.
     *
     *  @param executor — A callback used to initialize the promise. 
     *  This callback is passed two arguments: a resolve callback used to resolve the promise
     *  with a value or the result of another promise, and a reject callback used to reject the 
     *  promise with a provided reason or error.
     */
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;

        const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
            }
        };

        const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {

        }
    }
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled — The callback to execute when the Promise is resolved.
     * @param onrejected — The callback to execute when the Promise is rejected.
     * @returns — A Promise for the completion of which ever callback is executed. 
     */
    then(onfulfilled,onrejected) {
        if(this.status === STATUS.FULFILLED){
            onfulfilled(this.value)
        }
        if(this.status === STATUS.REJECTED){
            onrejected(this.reason)
        }
    }
}

module.exports = Promise;
let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
  },e=>{
    console.log("fail",e);
  })

//success xxx

現在我們實現了一個簡單的Promise丢氢,可以在Promise中通過resolve函數傳遞普通值到then函數中傅联,并在then的onfullfilled中取出,或者通過reject傳遞在onrejected中取出疚察。并且實現Promise狀態(tài)從pendding到resolve或reject,不可逆蒸走。

實現鏈式調用

then(onfulfilled,onrejected) {
        let promise2 = new Promise((resolve,reject)=>{
            if(this.status === STATUS.FULFILLED){
                let x = onfulfilled(this.value)
                resolve(x)
            }
            if(this.status === STATUS.REJECTED){
                let x = onrejected(this.reason)
                reject(x)
            } 
            if(this.status === STATUS.PENDING){
                this.onResolvedCallbacks.push(()=>{
                    onfulfilled(this.value);
                });
                this.onrejectedCallbacks.push(()=>{
                    onrejected(this.reason);
                });
            }
        });
        return promise2;
        
    }
    
const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        };
const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        };
        
let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
    return "next"
  },e=>{
    console.log("fail",e);
  }).then(res=>{
    console.log("success1",res)
  })
  //success xxx
  //success1 next

處理返回值是Promise的情況 resolvePromise函數的實現

//解析x類型,決定promise2走成功還是失敗
function resolvePromise(promise2, x, resolve, reject) {
    console.log("x", x)
    if (x === promise2) {
        return reject(new TypeError("返回值錯誤貌嫡,不能使用同一個promise"))
    }
    //只有object或者function才能是Promise
    if ((typeof x === 'object' && typeof x != null) || typeof x === 'function') {
        let called = false;//成功或者失敗方法只能調用一次比驻,防止其他Promise實現錯誤
        try {
            let then = x.then;//通過判斷x上是否有then函數,判斷x是否是一個Promise
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    // resolve(y) y有可能還是promise 遞歸解析 直到是普通值為止
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                })
            } else {
                reject(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }

    } else {
        reject(x);
    }
}

引入Promise A+ 單元測試

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

npm install promises-aplus-tests
promises-aplus-tests myPromise.js
單元測試

如果單元測試通過岛抄,證明我們的Promise符合Promise A+規(guī)范

完整代碼

const STATUS = {
    "PENDING": "pending",
    "FULFILLED": "fulfilled",
    "REJECTED": "rejected"
}
//解析x類型别惦,決定promise2走成功還是失敗
function resolvePromise(promise2, x, resolve, reject) {
    if (x == promise2) {
        return reject(new TypeError("返回值錯誤,不能使用同一個promise"))
    }
    //只有object或者function才能是Promise
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called = false;//成功或者失敗方法只能調用一次夫椭,防止其他Promise實現錯誤
        try {
            let then = x.then;//通過判斷x上是否有then函數掸掸,判斷x是否是一個Promise
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    // resolve(y) y有可能還是promise 遞歸解析 直到是普通值為止
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                })
            } else {
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }

    } else {
        resolve(x);
    }
}

class Promise {

    /**
     *  Creates a new Promise.
     *
     *  @param executor — A callback used to initialize the promise. 
     *  This callback is passed two arguments: a resolve callback used to resolve the promise
     *  with a value or the result of another promise, and a reject callback used to reject the 
     *  promise with a provided reason or error.
     */
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };

        const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error)
        }
    }
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled — The callback to execute when the Promise is resolved.
     * @param onrejected — The callback to execute when the Promise is rejected.
     * @returns — A Promise for the completion of which ever callback is executed. 
     */
    then(onfulfilled, onrejected) {
        //判斷then傳遞的參數是否是函數,如果不是則包裝成一個函數
        onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : x => x;
        onrejected = typeof onrejected === 'function' ? onrejected : error => {
            throw error
        };
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === STATUS.FULFILLED) {
                setTimeout(() => { //創(chuàng)建一個宏任務蹭秋,保證promise2已完成創(chuàng)建
                    try {
                        let x = onfulfilled(this.value); //x的返回值有可能是一個新的Promise猾漫,需要進行判斷和處理
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error) //如果返回異常,捕獲并作為失敗結果返回
                    }
                    // resolve(x)
                }, 0);

            }
            if (this.status === STATUS.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                    // reject(x)
                }, 0);
            }

            if (this.status === STATUS.PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onfulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)

                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                });
            }
        });
        return promise2;

    }
    catch(errFn) {
        return this.then(null, errFn);
    }
}

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末感凤,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子粒督,更是在濱河造成了極大的恐慌陪竿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屠橄,死亡現場離奇詭異族跛,居然都是意外死亡,警方通過查閱死者的電腦和手機锐墙,發(fā)現死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門礁哄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人溪北,你說我怎么就攤上這事桐绒《崞ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵茉继,是天一觀的道長咧叭。 經常有香客問我,道長烁竭,這世上最難降的妖魔是什么菲茬? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮派撕,結果婚禮上婉弹,老公的妹妹穿的比我還像新娘。我一直安慰自己终吼,他們只是感情好镀赌,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衔峰,像睡著了一般佩脊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上垫卤,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天威彰,我揣著相機與錄音,去河邊找鬼穴肘。 笑死歇盼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的评抚。 我是一名探鬼主播豹缀,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慨代!你這毒婦竟也來了邢笙?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤侍匙,失蹤者是張志新(化名)和其女友劉穎氮惯,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體想暗,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡妇汗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了说莫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杨箭。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖储狭,靈堂內的尸體忽然破棺而出互婿,到底是詐尸還是另有隱情捣郊,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布擒悬,位于F島的核電站模她,受9級特大地震影響,放射性物質發(fā)生泄漏懂牧。R本人自食惡果不足惜侈净,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望僧凤。 院中可真熱鬧畜侦,春花似錦、人聲如沸躯保。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽途事。三九已至验懊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尸变,已是汗流浹背义图。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留召烂,地道東北人碱工。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像奏夫,于是被迫代替她去往敵國和親怕篷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容