手寫Promise

參考:Promise實現(xiàn)原理(附源碼)

promise的注意點:

Promise特點

1.狀態(tài)不可變

2.值穿透糠雨,then的回調(diào)resolve要是不是一個function而是基本類型吉执,那么這個基本類型就會傳遞給下一個then 關(guān)于promise的一道面試題

3.then的回調(diào)resolvethrow erro會跳到下一個then回調(diào)的reject

高階函數(shù):接收函數(shù)作為參數(shù)的函數(shù)

高階函數(shù)——廖雪峰

1.一個最簡單的高階函數(shù):

function add(x, y, f) {//add為高階函數(shù)
    return f(x) + f(y);
}

2.高階函數(shù)決定了回調(diào)的調(diào)用時間以及接收的參數(shù)

例如:高階函數(shù)fn接收函數(shù)callBack作為參數(shù),就決定了callBack的調(diào)用時間以及callBack的參數(shù)

調(diào)用fn時定義callBack

function fn(callBack) {//定義一個有回調(diào)的function——》fn
    //fn決定何時調(diào)用這個回調(diào)挟伙,以及對回調(diào)的傳參
    callBack(1,2)
}

fn(function (fir,sec) {//調(diào)用fn傳入回調(diào),決定回調(diào)拿到傳參的具體執(zhí)行
    return fir+sec
})

3.高階函數(shù)的回調(diào)接收的參數(shù)仍然是函數(shù)

例如:

定義高階函數(shù)fn,并且給fn的回調(diào)傳入函數(shù)

調(diào)用fn時決定回調(diào)的具體內(nèi)容,以及何時使用fn給的函數(shù)參數(shù)resolve仓蛆、reject

function fn(callBack) {//定義一個有回調(diào)的function——》fn
    //fn決定何時調(diào)用這個回調(diào),以及對回調(diào)的傳參
    let resolve=function(){
        console.log('resolve')
    }
    let reject=function(){
        console.log('reject')
    }
    
    callBack(resolve,reject)//對回調(diào)callBack的傳參為函數(shù)
    
}

fn(function (resolve,reject) {//調(diào)用fn傳入回調(diào)蝌诡,決定回調(diào)拿到傳參的具體執(zhí)行
    return resolve()//回調(diào)的定義中決定什么時候使用收到的傳參
})

Promise架構(gòu):

1.new Promise().then()

? 高階函數(shù)的使用:

new MyPromise(handle)的回調(diào)——>handles收到的參數(shù)為函數(shù)

new MyPromise會執(zhí)行calss MyPromise的構(gòu)造函數(shù),所以class MyPromise是在定義高階函數(shù)溉贿,并且回調(diào)接收到的參數(shù)也就是函數(shù)(resolve、reject)浦旱。

class MyPromise{//定義
    constructor(handle){
        handle(this.res,this.rej)
    }
    res(){}

    rej(){}
}
new MyPromise(function (resolve,reject) {//使用
    resolve(1)
}).then()

new MyPromise().then()表示執(zhí)行MyPromise的構(gòu)造函數(shù)之后立馬執(zhí)行then()

then根據(jù)MyPromise實例使用的是resolve()還是reject()來選擇回調(diào)

所以resolve(),reject改變MyPromise中的this.state宇色,then再根據(jù)this.state選擇執(zhí)行resolve還是reject

此步驟完成代碼如下:

const PENDING = 'PENDING'//進行中
const FULFILLED = 'FULFILLED'//已成功
const REJECTED = 'REJECTED' //已失敗

class MyPromise{
    constructor(handle){
        this._value = null
        this._status = PENDING
        handle(this._resolve.bind(this),this._reject.bind(this))
    }
    _resolve(val){
        if (this._status !== PENDING) return//狀態(tài)不可逆,只能從PENDING——》FULFILLED颁湖,不能從別的狀態(tài)到FULFILLED
        this._value=val
        this._status=FULFILLED
    }

    _reject(val){
        if (this._status !== PENDING) return//狀態(tài)不可逆宣蠕,只能從PENDING——》REJECTED,不能從別的狀態(tài)到REJECTED
        this._value= val
        this._status=REJECTED
    }
}

MyPromise.prototype.then=function (onFulfilled,onRejected) {
    switch (this._status) {
        case PENDING:break;
        case FULFILLED:onFulfilled(this._value);break;
        case REJECTED:onRejected(this._value);break;
    }

}

new MyPromise(function (resolve,reject) {
    resolve('call resolve')
}).then(function (val) {
    console.log(val)
})//call resolve

new MyPromise(function (resolve,reject) {
    reject('call reject')
}).then(function () {},function (val) {
    console.log(val)
})//call reject

? 注意:1. 高階函數(shù)對this的處理:handle(this._resolve.bind(this),this._reject.bind(this)) 傳遞_resolve_rejecthandle調(diào)用甥捺,那么它們的this會隨著hangle走抢蚀,所以此處應(yīng)該要bind

? 2.Promise的狀態(tài)不可變:由于只有_resolve_reject會修改狀態(tài),所以只要保證_resolve

镰禾、_reject修改狀態(tài)前狀態(tài)都為PENDING就可以做到狀態(tài)不可變

?

2.異步操作

? new MyPromise(handle1).then(handle1)是同步的皿曲,handle1執(zhí)行之后會立刻執(zhí)行then(),異步操作到任務(wù)隊列中等待執(zhí)行。但此時handle1的異步操作還沒有執(zhí)行羡微,沒有進行resolve谷饿、reject所以then中的回調(diào)也無法執(zhí)行。

? 此時引入Pending(進行中)妈倔、Fulfilled(已成功)博投、Rejected(已失敗)狀態(tài)盯蝴,resolve()可以將Pending轉(zhuǎn)變?yōu)?code>Fulfilled,同理reject()Pending轉(zhuǎn)變?yōu)?code>Rejected狀態(tài)

? new MyPromise(handle1).then(handle1)執(zhí)行到then()時如果有異步操作毅哗,那么狀態(tài)仍為Peding,此時將then的回調(diào)都儲存起來捧挺,等待resolve()虑绵、reject()執(zhí)行時再執(zhí)行。

此步驟完成代碼如下:

const PENDING = 'PENDING'//進行中
const FULFILLED = 'FULFILLED'//已成功
const REJECTED = 'REJECTED' //已失敗

class MyPromise{
    constructor(handle){
        this._value = null
        this._status = PENDING
        // 添加成功回調(diào)函數(shù)隊列
        this._fulfilledQueues = []
        // 添加失敗回調(diào)函數(shù)隊列
        this._rejectedQueues = []
        handle(this._resolve.bind(this),this._reject.bind(this))
    }
    _resolve(val){
        if (this._status !== PENDING) return//狀態(tài)不可逆闽烙,只能從PENDING——》FULFILLED翅睛,不能從別的狀態(tài)到FULFILLED
        this._value=val
        this._status=FULFILLED
        let cb
        while (cb=this._fulfilledQueues.shift()){//依次執(zhí)行成功隊列中的函數(shù),并清空隊列
            cb(val)
        }
    }

    _reject(val){
        if (this._status !== PENDING) return//狀態(tài)不可逆黑竞,只能從PENDING——》REJECTED捕发,不能從別的狀態(tài)到REJECTED
        this._value= val
        this._status=REJECTED
        let cb
        while (cb=this._rejectedQueues.shift()){//依次執(zhí)行失敗隊列中的函數(shù),并清空隊列
            cb(val)
        }
    }
}

//使用例子
MyPromise.prototype.then=function (onFulfilled,onRejected) {
    switch (this._status) {
        case PENDING: this._fulfilledQueues.push(onFulfilled)//待選擇很魂,待執(zhí)行的回頭添加到隊列
                      this._rejectedQueues.push(onRejected)
                      break
        case FULFILLED: onFulfilled(this._value);break;
        case REJECTED: onRejected(this._value);break;
    }

}

new MyPromise(function (resolve,reject) {
    setTimeout(function () {
        resolve('call resolve')
    },1000)

}).then(function (val) {
    console.log(val)
})//1秒之后輸出 call resolve

new MyPromise(function (resolve,reject) {
    setTimeout(function () {
        reject('call reject')
    },2000)

}).then(function () {},function (val) {
    console.log(val)
})//2秒之后輸出 call reject

3.鏈?zhǔn)秸{(diào)用

3.1鏈?zhǔn)?/h4>

? 定義then方法時:

? 讓then返回一個MyPromise對象可實現(xiàn)鏈?zhǔn)秸{(diào)用扎酷。

onFullfilled(val)onRejectedNext(val)的調(diào)用決定了new MyPromise().then().then(res,rej)中的第二個then的回調(diào)什么時候調(diào)用,收到的傳參是多少遏匆。

MyPromise.prototype.then(function(onFullfilled,onRejected){
    return new MyPromise(function(onFullfilledNext,onRejectedNext){
        onFullfilledNext(val)//onRejectedNext(val)
    })
})

3.2鏈?zhǔn)脚c異步

常見情況下法挨,onFullfilledNext的調(diào)用時間取決于onFullfilled的調(diào)用時間谁榜,onFullfilledNext(val)傳遞的參數(shù)valonFullfilled的返回值

但是在異步情況下,onFullfilled是傳入._fulfilledQueues隊列中等待執(zhí)行的,所以將onFullfilled打包在fulfilled中延遲調(diào)用凡纳,將fulfilled代替onFullfilled放入隊列窃植。fulfilledonFullfilledNext根據(jù)onFullfilled的返回值傳參。

此步驟代碼:

function isFunction(fn) {
    return typeof fn === 'function'
}
const PENDING = 'PENDING'//進行中
const FULFILLED = 'FULFILLED'//已成功
const REJECTED = 'REJECTED' //已失敗

class MyPromise{
    constructor(handle){
        this._value = null
        this._status = PENDING
        // 添加成功回調(diào)函數(shù)隊列
        this._fulfilledQueues = []
        // 添加失敗回調(diào)函數(shù)隊列
        this._rejectedQueues = []
        try{
            handle(this._resolve.bind(this),this._reject.bind(this))
        }catch (err) {
            this._reject(err)
        }

    }
    _resolve(val){

        const run=() => {
            if (this._status !== PENDING) return//狀態(tài)不可逆惫企,只能從PENDING——》FULFILLED撕瞧,不能從別的狀態(tài)到FULFILLED
            // 依次執(zhí)行成功隊列中的函數(shù)陵叽,并清空隊列
            const runFulfilled = (value) => {
                let cb;
                while (cb = this._fulfilledQueues.shift()) {
                    cb(value)
                }
            }

            // 依次執(zhí)行失敗隊列中的函數(shù)狞尔,并清空隊列
            const runRejected = (error) => {
                let cb;
                while (cb = this._rejectedQueues.shift()) {
                    cb(error)
                }
            }

            if(val instanceof MyPromise){
                val.then((value)=>{
                    this._value=value
                    this._status=FULFILLED
                    runFulfilled(value)
                },(err)=>{
                    this._value=err
                    this._status=REJECTED
                    runRejected(err)
                })
            }else {
                this._value=val
                this._status=FULFILLED
                runFulfilled(val)
            }
        }
        setTimeout(run,0)

    }

    _reject(err){
        if (this._status !== PENDING) return//狀態(tài)不可逆,只能從PENDING——》REJECTED巩掺,不能從別的狀態(tài)到REJECTED

        this._value= err
        this._status=REJECTED
        let cb
        while (cb=this._rejectedQueues.shift()){//依次執(zhí)行失敗隊列中的函數(shù)偏序,并清空隊列
            cb(err)
        }
    }
}

MyPromise.prototype.then=function (onFulfilled,onRejected) {
    return new MyPromise( (onFulfilledNext,onRejectedNext) => {
        let fulfilled=(value) => {
            try {
                let result = onFulfilled(value)
                    onFulfilledNext(result)
            }catch (err) {
                onRejectedNext(err)
            }
        }

        let rejected=(value) => {
            try{
                let result =onRejected(value)
                    onRejectedNext(result)
            }catch (err) {
                onRejectedNext(err)
            }
        }

        switch(this._status){
            case PENDING : this._fulfilledQueues.push(fulfilled)
                this._rejectedQueues.push(rejected)
                break
            case FULFILLED :fulfilled(this._value)
                break
            case REJECTED : rejected(this._value)
                break
        }
    })

}

//使用例子
new MyPromise(function (resolve,reject) {
    setTimeout(function () {
        resolve(new MyPromise(function (resolve) {
            resolve('promise in resolve')
        }))
    },1000)

}).then(function (val) {
    console.log('first then,get message :'+val)//1秒之后輸出 first then,get message :promise in resolve
    return 'val form 1st then to 2nd then'
}).then(function (val) {
    console.log('second then,get message:'+val)//1秒之后輸出 second then,get message:val form 1st then to 2nd then
})

3.3值穿透與then的回調(diào)返回MyPromise對象

1.onFullfilled——》對應(yīng)調(diào)用onFullfilledNext

onFullfilled也就是第一個thenres回調(diào)

? onFullfilled不為function時:值穿透,直接onFullfilledNext(onFullFiled)

? onFullfilled為function時:1.如果返回值不為Promise胖替,onFullfilledNext(返回值)

? 2.如果返回值為Promise時研儒,返回值.then(onFullfilled)。將onFullfilledNext傳給then當(dāng)回調(diào)独令,由Promise決定何時將執(zhí)行權(quán)傳遞給then

?

2.onRejected——》對應(yīng)調(diào)用onRejectedNext

onRejected也就是then的rej回調(diào)

? onRejected不為function時:值穿透端朵,直接調(diào)用onRejectedNext(onRejected)

? onRejectedfunction時:1.返回值不為Promise,調(diào)用onRejectedNext(返回值)

? 2.返回值為Promise,調(diào)用返回值.then(onRejectedNext)

JS錯誤處理機制

4.new Promise中resolve傳遞的值為Promise的實例

resolve(new Promise)

第一個Promise中狀態(tài)取決于resolve中的Promise

也就是說resolve中的Promise的then回調(diào)執(zhí)行時就能確定第一個Promise的狀態(tài)

例如下面這種使用形式:

new Promise(function (resolve,reject) {
   return resolve(new Promise(function () {
       
   }))
}).then(function () {

},function () {

})

所以在實現(xiàn)MyPromise中的_resolve時燃箭,如果_resolve(val)中的值為Promise的實例,instanceof val=== MyPromise 那么在val.then()的兩個回調(diào)中改變MyPromise的狀態(tài)

代碼

function isFunction(fn) {
    return typeof fn === 'function'
}
const PENDING = 'PENDING'//進行中
const FULFILLED = 'FULFILLED'//已成功
const REJECTED = 'REJECTED' //已失敗

class MyPromise{
    constructor(handle){
        this._value = null
        this._status = PENDING
        // 添加成功回調(diào)函數(shù)隊列
        this._fulfilledQueues = []
        // 添加失敗回調(diào)函數(shù)隊列
        this._rejectedQueues = []
        try{
            handle(this._resolve.bind(this),this._reject.bind(this))
        }catch (err) {
            this._reject(err)
        }

    }
    _resolve(val){

        const run=() => {
            if (this._status !== PENDING) return//狀態(tài)不可逆冲呢,只能從PENDING——》FULFILLED,不能從別的狀態(tài)到FULFILLED
            // 依次執(zhí)行成功隊列中的函數(shù)招狸,并清空隊列
            const runFulfilled = (value) => {
                let cb;
                while (cb = this._fulfilledQueues.shift()) {
                    cb(value)
                }
            }

            // 依次執(zhí)行失敗隊列中的函數(shù)敬拓,并清空隊列
            const runRejected = (error) => {
                let cb;
                while (cb = this._rejectedQueues.shift()) {
                    cb(error)
                }
            }

            if(val instanceof MyPromise){
                val.then((value)=>{
                    this._value=value
                    this._status=FULFILLED
                    runFulfilled(value)
                },(err)=>{
                    this._value=err
                    this._status=REJECTED
                    runRejected(err)
                })
            }else {
                this._value=val
                this._status=FULFILLED
                runFulfilled(val)
            }
        }
        setTimeout(run,0)

    }

    _reject(err){
        if (this._status !== PENDING) return//狀態(tài)不可逆,只能從PENDING——》REJECTED裙戏,不能從別的狀態(tài)到REJECTED

        this._value= err
        this._status=REJECTED
        let cb
        while (cb=this._rejectedQueues.shift()){//依次執(zhí)行失敗隊列中的函數(shù)乘凸,并清空隊列
            cb(err)
        }
    }
}

MyPromise.prototype.then=function (onFulfilled,onRejected) {
    return new MyPromise( (onFulfilledNext,onRejectedNext) => {
        let fulfilled=(value) => {
            if(!isFunction(onFulfilled)) {
                onFulfilledNext(value) //值穿透
                return
            }

            try {
                let result = onFulfilled(value)
                if(result instanceof MyPromise){
                    result.then(onFulfilledNext,onRejectedNext)
                }else {
                    onFulfilledNext(result)
                }
            }catch (err) {
                onRejectedNext(err)
            }
        }

        let rejected=(value) => {
            if(!isFunction(onFulfilled)) {
                onRejectedNext(value)
                return
            }
            try{
                let result =onRejected(value)
                if(result instanceof  MyPromise){
                    result.then(onFulfilledNext,onRejectedNext)
                } else {
                    onRejectedNext(result)
                }
            }catch (err) {
                onRejectedNext(err)
            }
        }
        switch(this._status){
            case PENDING : this._fulfilledQueues.push(fulfilled)
                           this._rejectedQueues.push(rejected)
                           break
            case FULFILLED :fulfilled(this._value)
                            break
            case REJECTED : rejected(this._value)
                            break
        }
    })
}

//使用例子
new MyPromise(function (resolve,reject) {
    setTimeout(function () {
        resolve(new MyPromise(function (resolve) {
            resolve('promise in resolve')
        }))
    },1000)

}).then(function (val) {
    console.log('first then,get message :'+val)//1秒之后輸出 first then,get message :promise in resolve
    return new MyPromise(function (resolve) {
        setTimeout(()=>{
            return resolve('promise in then')
        },1000)

    })
}).then(function (val) {
    console.log('second then ,get message:'+val)//2秒之后輸出 second then ,get message:promise in then
})

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市累榜,隨后出現(xiàn)的幾起案子营勤,更是在濱河造成了極大的恐慌,老刑警劉巖壹罚,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葛作,死亡現(xiàn)場離奇詭異,居然都是意外死亡渔嚷,警方通過查閱死者的電腦和手機进鸠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形病,“玉大人客年,你說我怎么就攤上這事霞幅。” “怎么了量瓜?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵司恳,是天一觀的道長。 經(jīng)常有香客問我绍傲,道長扔傅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任烫饼,我火速辦了婚禮猎塞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杠纵。我一直安慰自己荠耽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布比藻。 她就那樣靜靜地躺著铝量,像睡著了一般。 火紅的嫁衣襯著肌膚如雪银亲。 梳的紋絲不亂的頭發(fā)上慢叨,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音务蝠,去河邊找鬼拍谐。 笑死,一個胖子當(dāng)著我的面吹牛请梢,可吹牛的內(nèi)容都是我干的赠尾。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼毅弧,長吁一口氣:“原來是場噩夢啊……” “哼气嫁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起够坐,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤寸宵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后元咙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梯影,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年庶香,在試婚紗的時候發(fā)現(xiàn)自己被綠了甲棍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡赶掖,死狀恐怖感猛,靈堂內(nèi)的尸體忽然破棺而出七扰,到底是詐尸還是另有隱情,我是刑警寧澤陪白,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布颈走,位于F島的核電站,受9級特大地震影響咱士,放射性物質(zhì)發(fā)生泄漏立由。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一序厉、第九天 我趴在偏房一處隱蔽的房頂上張望锐膜。 院中可真熱鬧,春花似錦脂矫、人聲如沸枣耀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牺堰,卻和暖如春拄轻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟葫。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工恨搓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筏养。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓斧抱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渐溶。 傳聞我的和親對象是個殘疾皇子辉浦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350