手寫 Promise 系列 --- 1

Promise 是 ES6 推出的用于解決callback嵌套層級太深問題的一種異步方案.

基本使用如下.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

myPromise.then(data=>{
    console.log(data)
},err=>{
    console.log(err)
})

先不考慮那么多,也不像很多博客里寫的那種,一上來就給非常完美的思路和非常標(biāo)準(zhǔn)的 Promise/A+ 規(guī)范.

我就按照自己思路一點(diǎn)點(diǎn)的來做.


step 01 從代碼代用級別摸清內(nèi)部構(gòu)造

  • 一個(gè) Promise 對象,有三個(gè)狀態(tài).
  • 初始狀態(tài)是 pending.
  • 成功狀態(tài)是 resolve.
  • 失敗狀態(tài)是 reject.
image.png

然后看上述寫的代碼.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

  • new Promise () 是一個(gè)構(gòu)造函數(shù),沒什么好說的.
function MyPromise () {

}

// 肯定是 
let my_promise = new MyPromise()

  • 這個(gè)構(gòu)造函數(shù)接受的參數(shù)也是一個(gè)函數(shù),也沒什么好說的.
function MyPromise (func) {
    typeof func === 'function' ===> true
}
  • 在這個(gè)函數(shù)參數(shù)里我們聲明了 resolve 和 reject 兩個(gè)形參.
function MyPromise((resolve,reject) => {
    typeof reject === 'function'  // true
    typeof resolve === 'function' // true
})
  • 在后續(xù)的使用參數(shù)的過程中,我們可以知道,是構(gòu)造函數(shù)傳入的執(zhí)行器,在內(nèi)部調(diào)用了這兩個(gè)reject 和 resolve
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
        reslove(data) // or reject (err)
    },1000)
})

  • reject 和 resolve 肯定不是由傳遞進(jìn)去的參數(shù)自己提供的.
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
    // resovle 哪來的?
        reslove(data) // or reject (err) // reject 哪來的?
    },1000)
})

  • 那么就只有一個(gè)可能,那是 Promise 內(nèi)部提供的兩個(gè)功能函數(shù).
function MyPromise () {
    function resolve () {}
    function reject () {}
}
  • 其中 resolve 函數(shù)別的不管,起碼應(yīng)該會把當(dāng)前 promise 對象的狀態(tài)由 pending --> resolve reject 同理, 從 pending --> reject
function MyPromise () {
    function resolve () {
        // pending ---> resolve
    }
    function reject () {
        // pending ---> reject
    }
}

  • 調(diào)用 then 實(shí)例方法,會將兩種狀態(tài)的回調(diào)函數(shù)傳遞進(jìn)去.
new MyPromise(executor).then(resolveData=>{},rejectData=>{})

step 02 根據(jù)上述推理,猜測內(nèi)部寫法.

1. 首先是一個(gè)構(gòu)造函數(shù)

function MyPromise () {

}

2. 其次需要接受一個(gè)執(zhí)行器.

function MyPromise (executor) {
    
}

3. 實(shí)例化出來的 Promise 對象有三種狀態(tài)

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

4. 一開始 promise 對象是 pending 狀態(tài)

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
}

5. 內(nèi)部起碼得有兩個(gè) reject 和 resolve.要不然executor里面的reject 和 resolve 哪來的? 或者把這兩個(gè)方法

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
    const that = this // 掛載 this
    function resolve () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.PENDING
        }
    }
    
    function reject () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.REJECT
        }
    }
}

或者覺得掛載 this 麻煩,我就把 resolve 和 reject 掛在 prototype 上.

MyPromise.prototype.reject = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

MyPromise.prototype.resolve = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

到目前為止,我們可以通過 executor 內(nèi)部調(diào)用 reject 或者 resolve 修改某個(gè) Promise 的狀態(tài)了.

在回到我們使用 ES6 的 Promise 代碼中.

new Promise ((resolve,reject) => {
    resolve(someData) // or reject(someError)
}) 

可以知道 reslove 和 reject 是可以接受參數(shù)的.

  • 對于 resolve 來說,參數(shù)就是 executor 執(zhí)行成功返回的的值.
  • 對于 reject 來說,參數(shù)就是 executor 執(zhí)行失敗返回的值.

6. 給 reject 和 resolve 來設(shè)置一個(gè)形參

// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

7.我們通過 then 傳遞了兩個(gè)函數(shù),第一個(gè)是成功的回調(diào). 第二個(gè)是失敗的回調(diào).

MyPromise.prototype.then = function (onResolve,onReject) {
    this.onResloveCallback = onResolve
    this.onRejectCallback = onReject
}


在回到 ES6 的 Promise 調(diào)用代碼中.

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve(data形參) // reject(失敗形參)
    },1000)
})

p.then(data=>{
    data === data 形參
})

8.resolve的形參傳遞給了then的第一個(gè)回調(diào)函數(shù)的參數(shù),error傳遞給了then的第二個(gè)回調(diào)函數(shù)的參數(shù)

// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
        this.error = error
    }
}

// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
        this.data = data
    }
}


9. 狀態(tài)改變完成之后,就需要調(diào)用從then函數(shù)接受過來的兩個(gè)函數(shù)參數(shù)了(reject&resolve)

// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 該狀態(tài)
        this.error = error // 記錄值
        this.onRejectCallback(this.error) // 調(diào)用回調(diào)
    }
}

// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改狀態(tài)
        this.data = data // 記錄值
        this.onResolveCallback(data) // 調(diào)用回調(diào)
    }
}

完整的代碼

function MyPromise (executor) {
    this.state = this.states.PENDING
    this.value = null // 用于記錄executor內(nèi)部的reject or resolve 傳遞回來的值.
    this.onRejectCallback = null
    this.onResolveCallback = null
    
    
    executor(this.resolve.bind(this),this.reject.bind(this))

}

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 該狀態(tài)
        this.error = error // 記錄值
        this.onRejectCallback(this.error) // 調(diào)用回調(diào)
    }
}

// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改狀態(tài)
        this.data = data // 記錄值
        this.onResolveCallback(data) // 調(diào)用回調(diào)
    }
}



step 03 . 測試MyPromise 的 resolve 和 reject

測試調(diào)用自定義的promise ---> pending --> resolve

new MyPromise((resolve, reject) => { 
  setTimeout(() => {
    resolve(1)
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

正確輸出.

image.png

測試調(diào)用自定義的promise ---> pending --> reject

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('error')
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

錯(cuò)誤輸出.

image.png

第一階段手寫 Promise 達(dá)成目標(biāo):

  • 狀態(tài)改變 [?]
  • 正確的調(diào)用then的函數(shù)接受的兩個(gè)回調(diào)函數(shù).[?]

補(bǔ)充一點(diǎn):

如果把 reject 和 resolve 寫在 prototype 上,然后直接通過 this.reject/resolve 傳遞會導(dǎo)致 this 指向的錯(cuò)誤(obj.method當(dāng)函數(shù)傳遞,只能傳 method, 不會傳 obj,真正的純函數(shù)傳遞).所以,上述代碼一定要寫成.

executor(this.resolve.bind(this),this.reject.bind(this))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,839評論 1 56
  • 一、Promise的含義 Promise在JavaScript語言中早有實(shí)現(xiàn),ES6將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法...
    Alex灌湯貓閱讀 889評論 0 2
  • 你不知道JS:異步 第三章:Promises 在第二章,我們指出了采用回調(diào)來表達(dá)異步和管理并發(fā)時(shí)的兩種主要不足:缺...
    purple_force閱讀 2,261評論 0 4
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,575評論 0 8
  • Promiese 簡單說就是一個(gè)容器,里面保存著某個(gè)未來才會結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,語法上說,Pr...
    雨飛飛雨閱讀 3,494評論 0 19

友情鏈接更多精彩內(nèi)容