ES5之理解與手寫實現(xiàn)Promise

  • 誕生:
    • commonJS社區(qū)提出Promise規(guī)范苫纤,用于統(tǒng)一處理異步回調(diào),后被列入ECMAScript2015標準
  • 主要特點:
    • 本質(zhì)就是Promise類纲缓,用于表示任務最終結(jié)果是成功還是失敗
    • 鏈式調(diào)用卷拘,解決大量的回調(diào)嵌套,避免回調(diào)地獄祝高,代碼結(jié)構(gòu)清晰
    • 同步與異步回調(diào)均可處理
  • 狀態(tài)(state):
    • pending(等待栗弟,promise的初始狀態(tài))
    • fulfuilled(成功)
    • rejected(失敗)

只存在pending-->fulfuilled 或者 pending-->rejected褂策。fulfuilled 與 rejected 只會執(zhí)行一個横腿。

  • 手寫實現(xiàn)(參考系統(tǒng)promise):
    • 原型對象函數(shù)
      • excutor函數(shù)
        需要立即執(zhí)行,所以在構(gòu)造函數(shù)中直接調(diào)用
      • reslove 與 reject
        使用箭頭函數(shù)斤寂,保留this指向
      • then函數(shù)
        判斷state耿焊,處理同步和異步(異步必須在回調(diào)后才會改變state)
      • finally函數(shù)
        無論promise對象返回成功還是失敗,finally都會被執(zhí)行一次遍搞,并且回調(diào)結(jié)果可以執(zhí)行then函數(shù)
      • catch函數(shù)
        then函數(shù)不執(zhí)行success回調(diào)版本
    • 靜態(tài)函數(shù)
      • all
        處理異步并發(fā)罗侯,按照異步代碼調(diào)用順序得到異步代碼執(zhí)行結(jié)果,參數(shù)為數(shù)組溪猿,數(shù)組值的順序即結(jié)果的順序钩杰,返回值也是promise對象
      • resolve
        對value進行promise包裝,返回值為promise
// promise的三種狀態(tài)
// pending 任何回調(diào)之前
const PENDING = "pending"
// fulfuilled 成功回調(diào)
const FULFUILLED = "fulfuilled"
// rejected 失敗回調(diào)
const REJECTED = "rejected"

// promise是類對象
class MyPromise {
    constructor (excutor) {
        // excutor異常處理
        try {
            // 外部excutor對象诊县,傳入兩個函數(shù)對象讲弄,對應 reslove  reject
            excutor(this.reslove, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    // 初始化狀態(tài)
    status = PENDING
    // 存放所有的成功回調(diào)函數(shù)
    sucCallBack = []
    // 存放所有的失敗回調(diào)函數(shù)
    failCallBack = []

    value = undefined
    reason = undefined

    reslove = value => {
        // 用于阻止非 pending 的調(diào)用(執(zhí)行 resolve 和 reject 必須二選一)
        if (this.status !== PENDING) return
        
        this.status = FULFUILLED
        this.value = value
        // FIFO
        while (this.sucCallBack.length) {
            this.sucCallBack.shift()()
        }
    }

    reject = reason => {
        // 用于阻止非 pending 的調(diào)用(執(zhí)行 resolve 和 reject 必須二選一)
        if (this.status !== PENDING) return

        this.status = REJECTED
        this.reason = reason
        // FIFO
        while (this.failCallBack.length) {
            this.failCallBack.shift()()
        }
    }
    // 是能被鏈式調(diào)用的,因此then有返回值依痊,是promise
    then (sucCallBack, failCallBack) {
        // then 參數(shù)變?yōu)榭蛇x
        sucCallBack = sucCallBack ? sucCallBack : value => value
        failCallBack = failCallBack ? failCallBack : reason => { throw reason }
        // 需要返回新的promise避除,用于實現(xiàn)then 鏈式調(diào)用
        let newPromise = new MyPromise((resolve, reject) => {
            // 分情況討論:同步 和 異步 回調(diào)處理
            // 同步
            // 調(diào)用then之前,同步status狀態(tài)已發(fā)生變化
            if (this.status === FULFUILLED) {
                setTimeout(() => {
                    try {
                        // 成功回調(diào)函數(shù)
                        let newValue = sucCallBack(this.value)
                        resolvePromise(newPromise, newValue, resolve, reject)   
                    } catch (error) {
                        // 在新promise中處理異常
                        reject(error)
                    }
                }, 0);
            }
            else if (this.status === REJECTED) {

                setTimeout(() => {
                    try {
                        // 失敗回調(diào)函數(shù)
                        let newValue = failCallBack(this.reason)
                        resolvePromise(newPromise, newValue, resolve, reject)   
                    } catch (error) {
                        // 在新promise中處理異常
                        reject(error)
                    }
                }, 0);

                
            }
            // 異步
            else
            {
                // 異步status狀態(tài)未發(fā)生變化胸嘁,先對回調(diào)函數(shù)做保存
                this.sucCallBack.push(() => {
                    setTimeout(() => {
                        try {
                            // 成功回調(diào)函數(shù)
                            let newValue = sucCallBack(this.value)
                            resolvePromise(newPromise, newValue, resolve, reject)   
                        } catch (error) {
                            // 在新promise中處理異常
                            reject(error)
                        }
                    }, 0);
                })
                this.failCallBack.push(() => {
                    failCallBack
                })
            }
        })
        return newPromise
    }
    // 無論promise對象返回成功還是失敗瓶摆,finally都會被執(zhí)行一次
    finally (callback) {
        // then 的返回值是promise
        // 不管value是promise對象還是普通值,一律返回promise對象性宏,保證異步promise的執(zhí)行順序
        return this.then(value => {
            return MyPromise.resolve(callback()).then(()=>value)
        },reason => {
            return MyPromise.resolve(callback()).then(()=>{throw reason})
        })
    }

    // 只調(diào)用失敗
    catch (failCallBack) {
        return this.then(undefined, failCallBack)
    }
    // 處理異步并發(fā)群井,按照異步代碼調(diào)用順序得到異步代碼執(zhí)行結(jié)果,參數(shù)為數(shù)組毫胜,數(shù)組值的順序即結(jié)果的順序书斜,返回值也是promise對象
    static all(array) {
 
        let result = []
        let index = 0
        
        return new MyPromise((resolve, reject)=>{

            function addData(key, value) {
                result[key] = value
                index ++
                // 保證所有(同步于異步)結(jié)果都獲取到后诬辈,在resolve
                if (index === array.length) {
                    resolve(result)
                }
            }
            array.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    // promise對象
                    item.then(value=>addData(index,value),reason=>reject(reason))
                }
                else 
                {
                    // 普通值
                    addData(index, item)
                }
            })
        })
    }
    // 對value進行promise包裝,返回值為promise
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise(reslove=>{
            reslove(value)
        })
    }
    
}

function resolvePromise(newPromise, newValue, resolve, reject) {
    // 分情況討論:
    // 原promise(防止循環(huán)調(diào)用)
    if (newPromise === newValue) {
        // 在新promise中處理異常
        reject("cycle call promise")
    }
    // MyPromise
    if (newValue instanceof MyPromise) {
        // 將狀態(tài)傳遞給下一個promise對象
        // ?? ---- 實現(xiàn)then鏈式異步順序調(diào)用
        // newValue.then(value=>resolve(value),value=>reject(value))
        newValue.then(resolve,reject)
    }
    // 普通值
    else
    {
        // 在新promise中處理成功回調(diào)
        resolve(newValue)
    }
}
module.exports = MyPromise
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菩佑,一起剝皮案震驚了整個濱河市自晰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稍坯,老刑警劉巖酬荞,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞧哟,居然都是意外死亡混巧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門勤揩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咧党,“玉大人,你說我怎么就攤上這事陨亡“猓” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵负蠕,是天一觀的道長蛙埂。 經(jīng)常有香客問我,道長遮糖,這世上最難降的妖魔是什么绣的? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮欲账,結(jié)果婚禮上屡江,老公的妹妹穿的比我還像新娘。我一直安慰自己赛不,他們只是感情好惩嘉,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著踢故,像睡著了一般宏怔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畴椰,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音鸽粉,去河邊找鬼斜脂。 笑死,一個胖子當著我的面吹牛触机,可吹牛的內(nèi)容都是我干的帚戳。 我是一名探鬼主播玷或,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼片任!你這毒婦竟也來了偏友?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤对供,失蹤者是張志新(化名)和其女友劉穎位他,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體产场,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡鹅髓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了京景。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窿冯。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖确徙,靈堂內(nèi)的尸體忽然破棺而出醒串,到底是詐尸還是另有隱情,我是刑警寧澤鄙皇,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布芜赌,位于F島的核電站,受9級特大地震影響育苟,放射性物質(zhì)發(fā)生泄漏较鼓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一违柏、第九天 我趴在偏房一處隱蔽的房頂上張望博烂。 院中可真熱鬧,春花似錦漱竖、人聲如沸禽篱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躺率。三九已至,卻和暖如春万矾,著一層夾襖步出監(jiān)牢的瞬間悼吱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工良狈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留后添,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓薪丁,卻偏偏與公主長得像遇西,于是被迫代替她去往敵國和親馅精。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353