天天看點

一步一步實作自己的Promise

一步一步實作自己的Promise

倘若你對Promise的使用還不是很熟練,我并不建議你就來實作自己的Promise,

你可以先閱讀

點選進入,希望可以給個star

整體流程的介紹

  1. 先定義整體結構
  2. 實作Promise構造函數
  3. 實作promise.then
  4. 實作promise.catch
  5. 實作promise.resovle
  6. 實作promise.reject
  7. 實作promise.all
  8. 實作promise.race

1.定義整體結構

  1. 先寫出構造函數,将Promise向外暴露
/*
自定義Promise函數子產品:IIFE
 */

(function (window) {
    /*
    Promise構造函數
    executor:執行器函數
     */
    function Promise(executor) {

    }

    // 向外暴露Promise
    window.Promise = Promise
})()

           
  1. 添加Promise原型對象上的方法
/*
    Promise原型對象的then
    指定一個成功/失敗的回調函數
    傳回一個新的promise對象
     */
    Promise.prototype.then = function(onResolved,onRejected){

    }

    /*
    Promise原型對象的.catch
    指定一個失敗的回調函數
    傳回一個新的promise對象
     */
    Promise.prototype.catch = function(onRejected){

    }
           
  1. 添加Promise函數對象上的方法
/*
    Promise函數對象的resovle方法
    傳回一個指定結果的promise對象
     */
    Promise.resolve = function(value){

    }

    /*
    Promise函數對象的reject方法
    傳回一個指定reason的失敗狀态的promise對象
    */
    Promise.reject = function(value){

    }

    /*
    Promise函數對象的all方法
    傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
    */
    Promise.all = function(value){

    }

    /*
    Promise函數對象的race方法
    傳回一個promise對象,狀态由第一個完成的promise決定
    */
    Promise.race = function(value){

    }
           

2. 實作Promise構造函數

  1. 衆所周知,構造函數裡由resolve和reject方法,而且這兩個方法會被傳入executor,并且executor立即同步執行
/*
    Promise構造函數
    executor:執行器函數
     */
    function Promise(executor) {

        function resovle() {

        }
        function reject() {

        }

        // 立即同步執行executor
        executor(resovle,reject)
    }

           
  1. 家喻戶曉的是,每個promise都有一個狀态可能為pending或resolved,rejected。是以需要添加個status,此外,當我們這樣使用Promise的時候,
// 例1
var promise = new Promise((resovle,reject)=>{
    
})

promise.then(resolve=>{},reject=>{})
           

這時執行到then,promise的狀态還是pending,這時要把值和then裡面的回調函數儲存起來,是以需要個data和callbacks

function Promise(executor) {

        var self = self

        self.status = 'pending' // 給promise對象指定status屬性,初始值為pending
        self.data = undefined // 給promise對象指定一個存儲結果的data
        self.callbacks = []  // 每個元素的結構:{onResolved(){},onRejected(){}}


        function resovle() {

        }
        function reject() {

        }

        // 立即同步執行executor
        executor(resovle,reject)
    }
           
  1. 婦孺皆知的是,在上面的例子1的基礎上,當我們執行resovle(value)時,
// 例2
var promise = new Promise((resolve,reject)=>{
    setTimeout(function () {
        resolve(1)
    })
})

promise.then(resolve=>{},reject=>{})

           

會把promise對象的status改為resovle,并且把value儲存到data,然後執行之前儲存的callbacks(上面說過當執行到then時,發現目前的promise是pending狀态,會把then裡的回調函數儲存到promise的callbacks裡)。

function resolve(value) {
            // 如果目前狀态不是pending,則不執行
            if(self.status !== 'pending'){
                return
            }
            // 将狀态改為resolved
            self.status = 'resolved'
            // 儲存value的值
            self.data = value

            // 如果有待執行的callback函數,立即異步執行回調函數onResolved
            if (self.callbacks.length>0){
                setTimeout(()=>{
                    self.callbacks.forEach(callbackObj=>{
                        callbackObj.onResolved(value)
                    })
                })
            }
        }
           
  1. 我們還知道,promise的狀态隻能改變一次,是以當執行resolve的時候要判斷是不是promise是不是pending的狀态,否則是不能執行的
function resolve(value) {
            // 如果目前狀态不是pending,則不執行
            if(this.status !== 'pending'){
                return 
            }
            // 将狀态改為resolved
            this.status = 'resolved'
            // 儲存value的值
            this.data = value

            // 如果有待執行的callback函數,立即異步執行回調函數onResolved
            if (this.callbacks.length>0){
                setTimeout(()=>{
                    this.callbacks.forEach(callbackObj=>{
                        callbackObj.onResolved(value)
                    })
                })
            }
        }
           
  1. 異曲同工之妙的是reject方法也是這個道理,是以這裡無需贅述
function reject(value) {
            // 如果目前狀态不是pending,則不執行
            if(self.status !== 'pending'){
                return
            }
            // 将狀态改為resolved
            self.status = 'rejected'
            // 儲存value的值
            self.data = value

            // 如果有待執行的callback函數,立即異步執行回調函數onResolved
            if (self.callbacks.length>0){
                setTimeout(()=>{
                    self.callbacks.forEach(callbackObj=>{
                        callbackObj.onRejected(value)
                    })
                })
            }
        }
           
  1. 我們又知道,當在執行executor的時候,如果執行異常的話,這個promise的狀态會直接執行reject方法。
// 例 3
var promise = new Promise((resolve,reject)=>{

    執行到這裡出錯了

    setTimeout(function () {
        resolve(1)
    })
})
           

要實作這個功能,我們可以在executor外讓try catch來捕獲

try{
            // 立即同步執行executor
            executor(resolve,reject)
        }catch (e) { // 如果執行器抛出異常,promise對象變為rejected狀态
            reject(e)
        }
           

好了,現在來測試一下,為了測試,我們需要簡單的實作下then,讓它往callbacks裡push then裡的回調函數

Promise.prototype.then = function(onResolved,onRejected){
        // 假設目前狀态還是pending狀态,将回調函數儲存起來
        this.callbacks.push({
            onResolved,
            onRejected
        })
    }
           

好了,現在測試下,發現成功。

// 例4
 let promise = new Promise((resolve,reject)=>{
        
        setTimeout(function () {
            // resolve(1)
            reject(1)
        },100)
    })

    promise.then(
        value=>{
            console.log("onResolved:",value);
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )

           

3. 實作then方法

盡人皆知的時,執行到then時,promise可能會是pending狀态,此時就要把then裡的回調函數儲存起來,也可能會是resolved或者rejected狀态,此時要執行onResolved或onRejected方法。

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        if(self.status === 'pending'){
            // promise目前狀态還是pending狀态,将回調函數儲存起來
            self.callbacks.push({
                onResolved(){onResolved(self.data)},
                onRejected(){onRejected(self.data)}
            })
        }else if(self.status === 'resolved'){
            setTimeout(()=>{
                onResolved(self.data)
            })
        }else{
            setTimeout(()=>{
                onResolved(self.data)
            })
        }

    }

           

而且我們知道,執行完then是要傳回一個新的promise的,而新的promise的狀态則由目前then的執行結果來确定。

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise目前狀态還是pending狀态,将回調函數儲存起來
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }
           

當目前的promise狀态為resolved的時候,則目前執行的onresolved函數由三種情況

  1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
// 例5
    let promise = new Promise((resolve,reject)=>{
        resolve(1)
    })

    promise.then(
        value=>{
            return value
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )
           

是以,我們可以這樣實作

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise目前狀态還是pending狀态,将回調函數儲存起來
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    const result = onResolved(self.data)
                    if (result instanceof Promise){

                    } else {
                    // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
                        resolve(result)
                    }
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }
           
  1. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果,如代碼所示,我們傳回一個新的promise。這個新的promise執行了resolve,是以傳回的promise的狀态是resolved的
// 例6
 let promise = new Promise((resolve,reject)=>{
        resolve(1)
    })

    promise.then(
        value=>{
            return new Promise((resolve,reject)=>{
                resolve(2)
            })
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )
           

是以我們可以這樣實作

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise目前狀态還是pending狀态,将回調函數儲存起來
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    const result = onResolved(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
                        resolve(result)
                    }
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }

           

在這裡說明一下:

result.then(
    value => {resolve(value)},
    reason => {reject(reason)}
)
           

由于我們在例6中執行了then裡的resolve函數,

将會導緻

value => {resolve(value)},

這個回調函數的執行,是以會把即将傳回的新的promise的data設定為value,會把狀态設定為resolved。

  1. 如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected,我們可以用try catch來實作
setTimeout(()=>{
    try{
        const result = onResolved(self.data)
        if (result instanceof Promise){
            // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
            result.then(
                value => {resolve(value)},
                reason => {reject(reason)}
            )
        } else {
            // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
            resolve(result)
        }
    }catch (e) {
      //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
        reject(e)
    }
})
           

異曲同工之妙的是當當status === ‘rejected’,道理一樣

setTimeout(()=>{
      try{
          const result = onRejected(self.data)
          if (result instanceof Promise){
              // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
              result.then(
                  value => {resolve(value)},
                  reason => {reject(reason)}
              )
          } else {
              // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
              resolve(result)
          }
      }catch (e) {
          //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
          reject(e)
      }
  })
           

到這裡,我們發現當執行resolve的時候,

onResolved(self.data)

onRejected(self.data)

執行時也會跟上面一樣的結果,可以說執行回調函數都要做以上判斷,是以我們要将

self.callbacks.push({
    onResolved(){onResolved(self.data)},
    onRejected(){onRejected(self.data)}
})
           

改成

if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
    onResolved(){
        try{
            const result = onResolved(self.data)
            if (result instanceof Promise){
                // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
                result.then(
                    value => {resolve(value)},
                    reason => {reject(reason)}
                )
            } else {
                // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
                resolve(result)
            }
        }catch (e) {
            //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
            reject(e)
        }
    },
           

到此,我們發現,相同的代碼太多了,是以有必要封裝一下

function handle(callback) {
    try{
        const result = callback(self.data)
        if (result instanceof Promise){
            // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
            result.then(
                value => {resolve(value)},
                reason => {reject(reason)}
            )
        } else {
            // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
            resolve(result)
        }
    }catch (e) {
        //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
        reject(e)
    }
}
           

這樣以來就清爽了很多

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
           /*
            調用指定回調函數的處理,根據執行結果。改變return的promise狀态
             */
            function handle(callback) {
                try{
                    const result = callback(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
                        resolve(result)
                    }
                }catch (e) {
                    //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
                    reject(e)
                }
            }
            if(self.status === 'pending'){
                // promise目前狀态還是pending狀态,将回調函數儲存起來
                self.callbacks.push({
                    onResolved(){
                        handle(onResolved)
                    },
                    onRejected(){
                        handle(onRejected)
                    }
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    handle(onResolved)
                })
            }else{ // 當status === 'rejected'
                setTimeout(()=>{
                    handle(onRejected)
                })
            }
        })

    }

           

另外,我們還知道,promise會發生直傳透,例如

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
           

運作結果: 1

解釋:.then 或者 .catch 的參數期望是函數,傳入非函數則會發生值穿透。值傳透可以了解為,當傳入then的不是函數的時候,這個then是無效的。

是以,要實作直傳透這個特性,我們可以這樣實作

添加這兩句來判斷要不要發生值傳透

onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
           
Promise.prototype.then = function(onResolved,onRejected){
        onResolved = typeof onResolved === 'function'? onResolved: value => value
        onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
        var self = this

        return new Promise((resolve,reject)=>{

            /*
            調用指定回調函數的處理,根據執行結果。改變return的promise狀态
             */
            function handle(callback) {
                try{
                    const result = callback(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
                        resolve(result)
                    }
                }catch (e) {
                    //  3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
                    reject(e)
                }
            }
            if(self.status === 'pending'){
                // promise目前狀态還是pending狀态,将回調函數儲存起來
                self.callbacks.push({
                    onResolved(){
                        handle(onResolved)
                    },
                    onRejected(){
                        handle(onRejected)
                    }
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    handle(onResolved)
                })
            }else{ // 當status === 'rejected'
                setTimeout(()=>{
                    handle(onRejected)
                })
            }
        })

    }

           

3.實作catch方法

婦孺皆知的是,catch方法的作用跟then裡的第二歌回調函數一樣,是以我們可以這樣來實作

Promise.prototype.catch = function(onRejected){
    return this.then(undefined,onRejected)
}
           

天啊,居然這麼簡單

4. 實作Promise.resolve

我們都知道,Promise.resolve方法可以傳三種值

  1. 不是promise
  2. 成功狀态的promise
  3. 失敗狀态的promise
Promise.resolve(1)
    Promise.resolve(Promise.resolve(1))
    Promise.resolve(Promise.reject(1))
           

實際上跟實作上面的then時有點像

Promise.resolve = function(value){
  return new Promise((resolve,reject)=>{
      if (value instanceof Promise){
          // 如果value 是promise
          value.then(
              value => {resolve(value)},
              reason => {reject(reason)}
          )
      } else{
          // 如果value不是promise
          resolve(value)
      }

  }

}
           

5.實作Promise.reject

實作這個比較簡單,傳回一個狀态為rejected的promise就好了

/*
Promise函數對象的reject方法
傳回一個指定reason的失敗狀态的promise對象
*/
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}

           

6.實作Promise.all

我們知道,這個方法會傳回一個promise

/*
    Promise函數對象的all方法
    傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
    */
    Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
           
        })
    }
  
           

而這個promise的狀态由周遊每個promise産生的結果決定

/*
    Promise函數對象的all方法
    傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
    */
    Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
            // 周遊promises,擷取每個promise的結果
            promises.forEach((p,index)=>{
                
            })
        })
    }
           

有兩種結果:

  1. 周遊到有一個promise是reject狀态,則直接傳回的promise狀态為rejected
Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
            // 周遊promises,擷取每個promise的結果
            promises.forEach((p,index)=>{
                p.then(
                    value => {
    
                    },
                    reason => { //隻要有一個失敗,return的promise狀态就為reject
                        reject(reason)
                    }
                )
            })
        })
    }
           
  1. 周遊所有的promise的狀态都為resolved,則傳回的promise狀态為resolved,并且還要每個promise産生的值傳遞下去
Promise.all = function(promises){
      const values = new Array(promises.length)
      var resolvedCount = 0 //計狀态為resolved的promise的數量
      return new Promise((resolve,reject)=>{
          // 周遊promises,擷取每個promise的結果
          promises.forEach((p,index)=>{
              p.then(
                  value => {
                      // p狀态為resolved,将值儲存起來
                      values[index] = value
                      resolvedCount++;
                      // 如果全部p都為resolved狀态,return的promise狀态為resolved
                      if(resolvedCount === promises.length){
                          resolve(values)
                      }
                  },
                  reason => { //隻要有一個失敗,return的promise狀态就為reject
                      reject(reason)
                  }
              )
          })
      })
  }
           

好像可以了,當其實這裡還有一個問題,就是all傳進去的數組不一定都是promise對象,可能是這樣的

all([p,2,3,p])
           

是以需要把不是promise的數字包裝成promise

Promise.all = function(promises){
        const values = new Array(promises.length)
        var resolvedCount = 0 //計狀态為resolved的promise的數量
        return new Promise((resolve,reject)=>{
            // 周遊promises,擷取每個promise的結果
            promises.forEach((p,index)=>{
                Promise.resolve(p).then(
                    value => {
                        // p狀态為resolved,将值儲存起來
                        values[index] = value
                        resolvedCount++;
                        // 如果全部p都為resolved狀态,return的promise狀态為resolved
                        if(resolvedCount === promises.length){
                            resolve(values)
                        }
                    },
                    reason => { //隻要有一個失敗,return的promise狀态就為reject
                        reject(reason)
                    }
                )
            })
        })
    }
           

7.實作Promise.race

這個方法的實作要比all簡單很多

/*
    Promise函數對象的race方法
    傳回一個promise對象,狀态由第一個完成的promise決定
    */
    Promise.race = function(promises){
        return new Promise((resolve,reject)=>{
            // 周遊promises,擷取每個promise的結果
            promises.forEach((p,index)=>{
                Promise.resolve(p).then(
                    value => {
                        // 隻要有一個成功,傳回的promise的狀态九尾resolved
                        resolve(value)

                    },
                    reason => { //隻要有一個失敗,return的promise狀态就為reject
                        reject(reason)
                    }
                )
            })
        })
    }

           
一步一步實作自己的Promise