天天看點

超詳細!一次性搞懂手寫Promise全過程

一、Promise的使用

在學習如何寫Promise之前,先要熟悉Promise的使用,然後一步一步去實作

【Promise的基本使用】:

1. Promise 是一個類 在執行這個類的時候需要傳遞一個執行器進去,執行器會立即執行

2. Promise中有三種狀态,分别是:等待pending 成功fulfilled 失敗rejected(狀态一旦确定不可改變)

3. 執行器接收兩個參數resolve(成功執行的方法)和reject(失敗執行的方法)

4. Promise擁有then方法,方法内部需判斷狀态,接收了兩個回調函數,如果成功調用成功的回調,如果失敗調用失敗的回調。then方法是被定義在原型對象中的

const promise = new Promise((resolve, reject) => {
    resolve('成功')
    // reject('失敗')
})
promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})
           

二、手寫Promise

【實作步驟】:

  1. 建立MyPromise類
  2. 通過構造函數constructor,在執行這個類的時候需要傳遞一個執行器進去并立即調用
  3. 定義resolve和reject(定義為箭頭函數:避免直接調用時this指向全局window問題)
  4. 定義狀态常量(成功fulfilled 失敗rejected 等待pending),初始化為pending。
  5. 完成resolve和reject函數的狀态改變(注意:需判斷目前狀态是否可以改變)
  6. MyPromise類中定義value和reason,用來儲存執行器執行成功和失敗的傳回值
  7. MyPromise類中添加then方法,成功回調有一個參數 表示成功之後的值;失敗回調有一個參數 表示失敗後的原因
  8. 處理異步邏輯(pending狀态下在then中将回調存起來)
  9. 實作then方法多次調用添加多個處理函數
  10. 實作then方法鍊式調用(寫一個函數方法專門判斷回調的結果是普通值還是promise,then方法傳回的仍然是一個promise)
  11. 處理promise傳回值各種類型情況(普通值,promise)
  12. then方法鍊式調用識别Promise對象自傳回
  13. Promise實作捕獲錯誤及then鍊式調用其他狀态代碼補充
  14. 将then方法的參數變為可選參數
  15. Promise.all
  16. Promise.resolve 傳回一個promise
  17.  finally方法 不管成功失敗都會執行一次
  18. catch方法的實作

【代碼】:

// 4. 定義狀态常量(成功fulfilled 失敗rejected 等待pending),初始化為pending。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 1 建立MyPromise類 
class MyPromise {
  // 2 通過構造函數constructor,在執行這個類的時候需要傳遞一個執行器進去并立即調用
  constructor(executor) {
    // 13 Promise實作捕獲錯誤
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  status = PENDING
  //  6. MyPromise類中定義value和reason,用來儲存執行器執行成功和失敗的傳回值
  value = null
  reason = null
  // 9. 實作then方法多次調用添加多個處理函數 初始化回調為數組依次執行
  successCallback = []
  failCallback = []
  // 3. 定義resolve和reject(定義為箭頭函數:避免直接調用時this指向全局window問題)
  resolve = value => {
    // 5. 完成resolve函數的狀态改變(注意:需判斷目前狀态是否可以改變)
    // 判斷目前狀态是否可改變
    if(this.status !== PENDING) return
    // 改變目前狀态
    this.status = FULFILLED
    // 儲存傳回值
    this.value = value
    // 執行成功回調
    while(this.successCallback.length) {
      this.successCallback.shift()(this.value)
    }
  }
  reject = reason => {
    // 5. 完成reject函數的狀态改變(注意:需判斷目前狀态是否可以改變)
    // 判斷目前狀态是否可改變
    if(this.status !== PENDING) return
    // 改變目前狀态
    this.status = REJECTED
    // 儲存傳回值
    this.reason = reason
    // 執行失敗回調
    while(this.failCallback.length) {
      this.failCallback.shift()(this.reason)
    }
  }
  // 7. MyPromise類中添加then方法,成功回調有一個參數 表示成功之後的值;失敗回調有一個參數 表示失敗後的原因
  then(successCallback, failCallback) {
    // 14 将then方法的參數變為可選參數
    successCallback = successCallback ? successCallback : value => this.value
    failCallback = failCallback ? failCallback : reason => {throw this.reason}
    // 10. 實作then方法鍊式調用(寫一個函數方法專門判斷回調的結果是普通值還是promise,then方法傳回的仍然是一個promise)
    let promise2 = new MyPromise((resolve, reject) => {
        // 判斷目前狀态 執行對應回調 異步情況下存儲目前回調等待執行
        if(this.status === FULFILLED) {
           // 異步
           setTimeout(() => {
            // 13 then方法捕獲錯誤
            try {
              // 異步擷取到promise2
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        } else if(this.status === REJECTED) {
          // 異步
          setTimeout(() => {
            // 13 then方法捕獲錯誤
            try {
              // 異步擷取到promise2
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        } else {
          // 8. 處理異步邏輯(pending狀态下在then中将回調存起來)
          this.successCallback.push(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
          this.failCallback.push(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        }
    })
    return promise2
  }
  // 17. finally方法 不管成功失敗都會執行一次
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.reject(callback()).then(() => { throw reason })
    })
  }
  // 18. catch
  catch(failCallback) {
    return this.then(undefined, failCallback)
  }
  // 15. Promise.all
  static all (array) {
    let result = []
    let index
    return new Promise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if(index === array.length) {
          resolve(result)
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i]
        if(current instanceof MyPromise) {
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          addData(i, array[i])
        }
      }
    })
  }
  // 16. Promise.resolve 傳回一個promise
  static resolve(value) {
    if(value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
}

// 處理promise傳回值各種類型情況(普通值,promise)
function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}
           

【對應驗證代碼】:

<!DOCTYPE html>
<html >
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./promise2.js"></script>
  <!-- <script src="./newPromise.js"></script> -->
  <script>
    // 1、基礎
    let promise = new MyPromise((resolve, reject) => {
      resolve('成功')
      // reject('失敗')
    })
    promise.then(value=>{
      console.log(value)
    },reason=>{
      console.log(reason)
    })

    // 2、異步
    let promise = new Promise((resolve, reject) => {
      setTimeout(() => { // 異步
        resolve('成功')
      }, 2000)
      // reject('失敗')
    })
    promise.then(value=>{
      console.log(value)
    },reason=>{
      console.log(reason)
    })

    // 3、then方法鍊式調用
    let promise = new MyPromise((resolve, reject) => {
      setTimeout(() => { // 異步
        resolve('成功')
      }, 2000)
      // reject('失敗')
    })
    function other () {
      return new MyPromise((resolve, reject) => {
        resolve('other')
      })
    }
    promise.then(value=>{
      console.log(value)
      return other()
    }).then(value => {
      console.log(value)
    })

    // 4、promise對象子傳回循環報錯
    var promise = new Promise(function(resolve, reject) {
      resolve(100)
    })
    var p1 = promise.then(function(value) {
      return p1
    })
    p1.then(value => {
      console.log(value)
    }, reason => {
      console.log(reason.message)
    })

    // 5、Promise實作捕獲錯誤及then鍊式調用其他狀态代碼補充
    var promise = new MyPromise(function(resolve, reject) {
      // setTimeout(() => {
      //   resolve('成功。。。')
      // },2000)
      throw new Error('executor error')
      // resolve('成功')
    })
    promise.then(value => {
      console.log(value)
      // throw new Error('then error')
      return 'aaa'
    }, reason => {
      console.log('報錯')
      console.log(reason)
      return '123'
    }).then(value => {
      console.log('value2')
      console.log(value)     
    }, reason => {
      console.log('報錯2')
      console.log(reason)
    })

    // 6、将then方法的參數變為可選參數
    var promise = new MyPromise(function(resolve, reject) {
      resolve(100)
    })
    promise.then().then(value => value).then(value => console.log(value))

    // 7、Promise.all
    // 按照異步代碼調用順序得到結果
    // 類直接調用的方法是靜态方法
    function p1() {
      return new Promise(function (resolve, reject) {
        setTimeout(function() {
          resolve('p1')
        },2000)
      })
    }
    function p2() {
      return new Promise(function (resolve, reject) {
        resolve('p2')
      })
    }
    Promise.all(['a', 'b', p1(), p2(), 'c']).then(function(result) {
      // result -> ['a', 'b', 'p1', 'p2', 'c']
      console.log(result)
    })

    // 8、Promise.resolve将給定的值轉換為promise對象
    function p1() {
      return new Promise(function (resolve, reject) {
        resolve('hello')
      })
    }
    Promise.resolve(10).then(value => console.log(value))
    Promise.resolve(p1()).then(value => console.log(value))

    // 9、finally方法無論promise執行成功或失敗finally都會執行一次
    // finally方法後可鍊式調用then方法拿到最終傳回的結果
    function p1() {
      return new Promise(function (resolve, reject) {
        reject('hello')
      })
    }
    p1().finally(() => {
      console.log('finally')
    }).then(value => {
      console.log(value)
    }, reason => {
      console.log('error')
      console.log(reason)
    })

    // 10、catch
    function p1() {
      return new Promise(function (resolve, reject) {
        resolve('hello')
      })
    }
    p1()
      .then(value => {
        return a
      })
      .then(value => console.log('value2:' + value))
      .catch(reason => console.log('catch:' + reason))
  </script>
</body>
</html>
           

繼續閱讀