Promise的祕密(Promise原理解析以及實現)

Axizs發表於2019-10-13

寫在前面

本篇文章將會帶大家從分解promise入手,一步步實現一個promise。但閱讀之前需要比較熟練地瞭解瞭解用法,結合用法看文章可能更容易理解。

結構

先看一下簡單的用法。

const promise = new Promise((resolve, reject) => {
   setTimeout(() => {
       resolve('success')
   })
})
.then(value => { ... }, reason => { ... })
.catch(error => { ... })

複製程式碼

Promise的建構函式接收了一個回撥,這個回撥就是下面要講到的執行器,執行器的引數resolve, reject也是兩個函式, 負責改變promise例項的狀態和它的值,then函式中的回撥在狀態改變後執行。

注意:不是then函式在狀態改變後執行,而是then中的回撥函式在狀態改變後執行。then方法會將其中的回撥放入執行佇列,promise的狀態改變後再將佇列中的函式一一執行。

如果要實現一個最簡單的promise類,內部結構都要包含什麼呢?

  • 狀態:fulfiled、rejected、pending。
  • 值:promise的值。
  • 執行器:提供改變promise狀態的入口。
  • resolve和reject方法:前者將promise改變為fulfiled,後者將其改變為rejected。可以在執行器內根據實際業務來控制是resolve或reject。
  • then方法:接收兩個回撥,onFulfilled, onRejected。分別在promise狀態變為fulfiled或rejected後執行,這裡涉及到將回撥註冊進兩個執行佇列的操作,後文會講到。
const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'

class NewPromise {
  constructor(handler) {
    this.state = PENDING
    this.value = undefined
    this.successCallback = []
    this.failureCallback = []
    try {
      handler(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      this.reject(e)
    }
  }

  // resolve和reject方法
  resolve(value) { ... }
  reject(reason) { ... }

  // then方法
  then(onFulfilled, onRejected) { ... }
}
複製程式碼

結構中的每個部分是如何實現的呢?

Promise的執行器

執行器是我們初始化promise時候傳入的回撥,是我們操作promise的入口,所以執行器的實現不復雜,也就是將我們傳入的回撥執行一下。

class NewPromise {
  ...
  handler(resolve.bind(this), reject.bind(this))
  ...
}
複製程式碼

實際上,執行器會接受兩個回撥,resolve和reject。它們真正起到改變promise狀態的作用。

resolve和reject

實際上是兩個函式,作為引數傳入到執行器中,所做的事情不復雜:

  • 改變promise的狀態
  • 將接收的值作為promise的value
  • 依次執行then中註冊的回撥
const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'

class NewPromise {
  constructor(handler) {
    this.state = PENDING
    this.value = undefined

    // 兩個佇列,後面會講到
    this.successCallback = []
    this.failureCallback = []
    try {
      // 執行器,由於resolve和reject中用到了this,這裡需要bind一下
      handler(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      this.reject(e)
    }
  }

  resolve(value) {
    if (this.state !== PENDING) return
    this.state = FULFILLED
    this.value = value
    // 用setTimeout模擬非同步方式
    setTimeout(() => {
      this.successCallback.forEach(item => {
        item(value)
      })
    })
  }

  reject(reason) {
    if (this.state !== PENDING) return
    this.state = REJECTED
    this.value = reason
    setTimeout(() => {
      this.failureCallback.forEach(item => {
        setTimeout(item(reason))
      })
    })
  }
}

複製程式碼

看一下它們的實現,改變狀態、賦值value。最重要的一點:迴圈執行then方法註冊到佇列中的回撥。 而規範中要求回撥以非同步方式執行,這樣保證在執行所有的回撥之前,所有回撥已經通過then註冊完成,所以這裡用setTimeout模擬了一下。

then方法:

(翻譯整理自Promise/A+ 規範

promise必須提供then方法來訪問這個promise當前或者最終的值。 then方法有兩個引數:onFulfilled, onRejected,都是可選的。關於這兩個引數,這裡有幾個規則:

onFulfilled, onRejected都不是函式的時候,必須被忽略

實際上忽略的意思也就是如果不是函式,預設給它賦值成函式,返回值為then所屬的promise的值。這樣是做是為了在then()函式未傳回撥的時候,可以將promise的值傳遞下去。場景如下:

promise(resolve => resolve('success'))
    .then()
    .then(function(value) {
     console.log(value)
    })
複製程式碼

具體實現上,在它不是函式的時候可以給它賦值一個預設函式,也可以直接呼叫新返回的promise中的resolve或reject將值傳下去,來達到忽略的效果。

onFulfilled是函式的時候

  • 必須在當前的promise的狀態變為fulfilled的時候被呼叫,promise被resolve的值也就是它的第一個引數
  • 不能在fulfilled之前被呼叫
  • 最多隻能被呼叫一次

onRejected是函式的時候

  • 必須在當前的promise的狀態變為rejected的時候被呼叫,promise被reject的值也就是它的第一個引數。不能在rejected之前被呼叫,
  • 最多隻能被呼叫一次。

then可能會被呼叫多次

  • 當promise狀態變為fulfilled,所有onFulfilled將會按照最開始在then方法中註冊的順序去呼叫
  • 當promise狀態變為rejected,所有onRejected將會按照最開始在then方法中註冊的順序去呼叫,就像下邊這樣:
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve('success'))
  })
  promise.then(res => {
    console.log(res, '第一次');
  })
  promise.then(res => {
    console.log(res, '第二次');
  })
複製程式碼

鑑於這種情況,需要在我們實現的promise內部維護兩個佇列,佇列中的元素是then方法內註冊的回撥函式(onFulfilled, onRejected),每呼叫一次then,就向佇列中註冊一個回撥,它們會在promise狀態改變時被依次執行。

返回一個promise,便於鏈式呼叫

 promise2 = promise1.then(onFulfilled, onRejected);
複製程式碼

then返回的promise(也就是promise2)的狀態,取決於其回撥函式(onFulfilled或onRejected)的返回值或者promise1的狀態,具體表現為:

  • onFulfilled或onRejected的返回值是一個值x,那麼promise2的狀態為resolve,值為x
  • 如果onFulfilled或onRejected執行出錯,並丟擲了錯誤物件e,那麼promise2的狀態為rejected,值為這個錯誤物件e
  • 如果onFulfilled不是一個函式,但promise1狀態變為fulfilled,那麼promise2狀態也為fulfilled,值與promise1相同
  • 如果onRejected不是一個函式,但promise1狀態變為rejected,那麼promise2狀態也為rejected,值與promise1相同(這個值是作為promise2的reason)

根據這些原則,我畫了一張圖放在了下文中

實現

上面我們認識了then方法,結合定義和平時的用法可以猜測出我們自己實現的promise內的then方法需要做下邊幾件事:

  • 返回一個新的promise例項
  • then所屬的Promise在pending狀態,將then的回撥(onFulfilled,onRejected)分別放入執行佇列等待執行,而這兩個佇列內的函式只有在then所屬的promise狀態被改變的時候執行。保證了規範中的onFulfilled, onRejected的執行時機。
  • then所屬的Promise狀態不為pending時,執行佇列中的回撥開始依次執行,然後根據已經改變的狀態以及回撥的返回值來決定新的promise的狀態
    • 舉例來說:
    const promise1 = new Promise((resolve, reject) =>{ ... })
    const promise2 = promise1.then(value => {
      return 'success'
    }, reason => {
      return 'failed'
    })
    複製程式碼
    假設promise1被resolve了,由於then中傳入了代表onFulfilled的回撥並且返回值為success,那麼promise2會被resolve,值為success。 假設promise2被reject了,由於then中傳入了代表onRejected的回撥並且返回值為failed,那麼promise2會被reject,reason是failed

下面一步步來實現then方法,先上結構:

class NewPromise {
  constructor(handler) {
    this.state = PENDING
    this.value = undefined

    // 兩個佇列,存放onFulfiled 和 onRejected
    this.successCallback = []
    this.failureCallback = []
    try {
      handler(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      this.reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    return new NewPromise((resolveNext, rejectNext) => {
      // pengding狀態向佇列中註冊回撥
      if (state === PENDING) {
        successCallback.push(onFulfilled)
        failureCallback.push(onRejected)
      }

      // 要保證在當前promise狀態改變之後,再去通過resolveNext或者rejectNext改變新的promise的狀態
      if (state === FULFILLED) {
        resolveNext(value)
      }
      if (state === REJECTED) {
        rejectNext(value)
      }
    })
  }
}
複製程式碼

上面的結構基本實現了then函式的大概邏輯,但是沒有實現根據onFulfilled, onRejected兩個回撥的執行結果來決定新的promise的狀態的效果, 只是將他們分別放到了各自的執行佇列中去。

最終then返回的promise的狀態和onFulfilled, onRejected的執行結果有關。我根據規範和實際情況整理了一張圖:

then.jpg

然後讓我們用程式碼來實現它(單以onFulfilled的執行情況舉例)

    try {
      // 正常情況
      if (typeof onFulfilled !== 'function') {
        // 不是函式,直接忽略,將then所屬的promise作為then返回的promise的值resolve來做到值的傳遞
        resolveNext(value)
      } else {
        // 獲取then函式回撥的執行結果
        const res = onFulfilled(value)
        if (res instanceof NewPromise) {
          // 當執行結果返回的是一個promise例項,等待這個promise狀態改變後再改變then返回的promise的狀態
          res.then(resolveNext, rejectNext)
        } else {
          // 當返回值是普通值,將其作為新promise的值resolve
          resolveNext(res)
        }
      }
    } catch (e) {
      // 出現異常,新promise的狀態變為rejected,reason就是錯誤物件
      rejectNext(e)
    }

複製程式碼

整個這一部分,需要放入佇列中等待then所屬的promise狀態改變再執行,從而改變then返回的promise的狀態。 所以,我們需要將這一塊包裝起來。整合起來就是:

class NewPromise {
  constructor(handler) {
    this.state = PENDING
    this.value = undefined

    // 兩個佇列,存放onFulfiled 和 onRejected
    this.successCallback = []
    this.failureCallback = []
    try {
      handler(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      this.reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    const { state, value } = this
    return new NewPromise((resolveNext, rejectNext) => {
      const resolveNewPromise = value => {
        try {
          // 正常情況
          if (typeof onFulfilled !== 'function') {
            // 不是函式,直接忽略,將then所屬的promise作為返回的新的promise的值resolve,來做到值的傳遞
            resolveNext(value)
          } else {
            // 獲取then函式回撥的執行結果
            const res = onFulfilled(value)
            if (res instanceof NewPromise) {
              // 當執行結果返回的是一個promise例項,等待這個promise狀態改變後再改變then返回的promise的狀態
              res.then(resolveNext, rejectNext)
            } else {
              // 當返回值是普通值,將其作為新promise的值resolve
              resolveNext(res)
            }
          }
        } catch (e) {
          // 出現異常,新promise的狀態變為rejected,reason就是錯誤物件
          rejectNext(e)
        }
      }
      const rejectNewPromise = reason => {
        try {
          // 正常情況
          if (typeof onRejected !== 'function') {
            // 不是函式,直接忽略,將then所屬的promise作為then返回的promise的值reject來做到值的傳遞
            rejectNext(reason)
          } else {
            // 獲取then函式回撥的執行結果
            const res = onRejected(reason)
            if (res instanceof NewPromise) {
              // 當執行結果返回的是一個promise例項,等待這個promise狀態改變後再改變then返回的promise的狀態
              res.then(resolveNext, rejectNext)
            } else {
              // 當返回值是普通值,將其作為新promise的值reject
              rejectNext(res)
            }
          }
        } catch (e) {
          // 出現異常,新promise的狀態變為rejected,reason就是錯誤物件
          rejectNext(e)
        }
      }
      if (state === PENDING) {
        this.successCallback.push(resolveNewPromise)
        this.failureCallback.push(rejectNewPromise)
      }
      // 要保證在當前promise狀態改變之後,再去改變新的promise的狀態
      if (state === FULFILLED) {
        resolveNewPromise(value)
      }
      if (state === REJECTED) {
        rejectNewPromise(value)
      }
    })
  }
}
複製程式碼

我們在自己的實現中是定義了兩個陣列作為任務佇列存放then註冊的回撥。而實際的promise中, then方法是將回撥註冊到微任務佇列中。等到promise狀態改變,再執行微任務佇列中的任務。微任務在概念上可以認為是非同步任務,這也印證了規範中then的回撥必須非同步執行的說法。

關於事件迴圈的一些知識點,我總結過一篇文章,今天,我明白了JS事件迴圈機制

catch函式

catch函式是用來處理異常的,當promise狀態變為rejected的時候,捕獲到錯誤原因。

那麼假設不用catch,也可以在then函式的第二個回撥中(onRejected)捕獲這個錯誤。 而且catch返回的是一個promise,所以與呼叫Promise.prototype.then(undefined, onRejected)的行為是一樣的

catch(onRejected) {
  return this.then(undefined, onRejected)
}
複製程式碼

resolve方法

Promise.resolve(value)返回的是一個promise物件,用於將傳入的值value包裝為promise物件。

那這樣做有什麼意義呢?實際上value可能是一個不確定的值,可能是promise也可能不是,沒準可以呼叫then方法,也沒準不可以。但是可以通過resolve方法將對value的操作行為統一起來。例如:

const promise = function() {
  if (shouldBePromise) {
    return new Promise(function(resolve, reject) {
        resolve('ok')
    })
  }
  return 'ok'
}
promise().then(() => {
    ...
})
複製程式碼

promise返回的結果取決於shouldBePromise,一旦shouldBePromise為false,那麼promise就返回了字串ok,下邊就不能呼叫then方法。

這個時候可以用Promise().resolve包起來,這樣promise返回的始終是一個promise例項,保證了then方法的順利呼叫。

Promise.resolve(promise()).then(() => {
    ...
})
複製程式碼

總結一下特點:Promise.resolve的引數如果:

  • 不傳,返回一個resolved狀態的Promise
  • 是一個thenable物件(即帶有"then"方法),返回的Promise的狀態將在這個物件狀態改變時改變,並且與該物件的狀態保持一致
  • 是普通值,返回一個resolved狀態的Promise,該promise的值為這個普通值
  • 是一個Promise物件,返回這個物件
  static resolve(value) {
    // value不存在,直接返回一個resolved狀態的promise
    if (!value) {
      return new NewPromise(function (resolve) {
        resolve()
      })
    }
    // value是promise例項,直接返回
    // 在這裡需要首先判斷是否是promise例項,再進行下邊的判斷
    // 因為我們自己構造的promise也是是object,也有then方法
    if (value instanceof NewPromise) {
      return value
    }
    // 是thenable物件,返回的新的promise例項需要在value狀態改變後再改變,且狀態跟隨value的狀態
    if (typeof value === 'object' && typeof value.then === 'function') {
      return new NewPromise((resolve, reject) => {
        value.then(resolve, reject)
      })
    }
    // value是普通值,返回新的promise並resolve這個普通值
    return new NewPromise(resolve => {
      resolve(value)
    })
  }
複製程式碼

reject方法

reject方法對比resolve相對簡單,它總是返回一個reject的promise物件,reject的原因是我們傳入的reason

  static reject(reason) {
    return new NewPromise((resolve, reject) => {
      reject(reason)
    })
  }
複製程式碼

finally方法

返回的是一個promise,作用是在promise結束時,無論結果是fulfilled或者是rejected,都會執行回撥函式。返回的新promise的狀態和值取決於原來的promise。

finally(callback) {
  // 返回值是promise物件,回撥在then中執行,也就符合了promise結束後呼叫的原則
  return this.then(
    // then方法的onFulfiled 和 onRejected都會被傳入,保證無論resolved或rejected都會被執行

    // 獲取到promise執行成功的結果,將這個結果作為返回的新promise的值
    res => NewPromise.resolve(callback())
        .then(() => {
          return res
        }),
    // 獲取執行失敗的結果。原理同上
    error => NewPromise.resolve(callback())
        .then(() => {
          throw error
        })
  )
}
複製程式碼

all方法

Promise.all(param) 接收一個引數陣列,返回一個新的promise例項。當引數陣列內的promise都resolve後或者引數內的例項執行完畢後,新返回的promise才會resolve。 陣列內任何一個promise失敗(rejected),新返回的promise失敗,失敗原因就是第一個失敗的promise的結果。

const p1 = Promise.resolve(1),
coint p2 = Promise.resolve(2),
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});
複製程式碼

由此可知,all方法需要返回一個新的promise例項,然後根據接收的引數陣列執行情況,控制新的promise例項的狀態與值

  static all(instanceList) {
    return new NewPromise((resolve, reject) => {
      // 定義存放結果的陣列
      const results = []
      let count = 0
      if (instanceList.length === 0) {
        resolve(results)
        return
      }
      instanceList.forEach((item, index) => {
        // 由於例項列表中的每個元素可能是各種各樣的,所以要用this.resolve方法包裝一層
        this.resolve(item).then(res => {
          results[index] = res
          count++
          // 當都執行完,resolve新返回的promise
          if (count === instanceList.length) {
            resolve(results)
          }
        }, error => {
          // 一旦有一個出錯,就reject新返回的promise
          reject(error)
        })
      })
    })
  }
複製程式碼

實現之後可以清楚的看到,all方法會並行執行所有promise,結果按傳入的promise陣列的順序輸出。這讓我想起了以前面試碰到的一個題目: 併發所有請求,按順序輸出。可以用Promise.all實現。但實際上會有請求失敗的情況,所以更好的方式是下邊要講到的Promise.allSettled。

allSettled方法

如果說finally提供了在單個promise是否成功都需要執行程式碼提供了一種方式,那麼allSettled就是為多個promise是否成功的場景提供了同樣的操作方式。

Promise.allSettled()方法返回一個promise,該promise在所有給定的promise已被解析或被拒絕後解析,並且每個物件都描述每個promise的結果。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
複製程式碼

不同於Promise.all的一旦有一個執行失敗,就無法獲得所有promise都執行完成的時間點的特點。allSettled方法下無論某個promise成功與否,一旦所有的promise都完成,就可以獲得這個時間點。因為其返回的新的promise,總是被resolve的,並且值是所有promise執行結果的描述。

[
  {"status":"rejected","reason":"失敗"},
  {"status":"fulfiled","value":"成功"}
]
複製程式碼

要實現它,需要在每個promise執行的時候把結果記錄下來放進一個陣列內,最後在所有promise執行完成後,resolve結果陣列,改變返回的新的promise的狀態。

  static allSettled(instanceList) {
    return new NewPromise((resolve, reject) => {
      const results = []
      let count = 0
      if (instanceList.length === 0) {
        resolve([])
        return
      }
      // 定義一個函式,來生成結果陣列
      const generateResult = (result, i) => {
        count++
        results[i] = result
        // 一旦全部執行完成,resolve新返回的promise
        if (count === instanceList.length) {
          resolve(results)
        }
      }
      instanceList.forEach((item, index) => {
        // 在每個promise完成後將狀態記錄到結果陣列中
        this.resolve(item).then(
          value => {
            generateResult({
              status: FULFILLED,
              value
            }, index)
          },
          reason => {
            generateResult({
              status: REJECTED,
              reason
            }, index)
          }
        )
      })
    })
  }
複製程式碼

race方法

與all方法類似,接受一個例項陣列為引數,返回新的promise。但區別是一旦例項陣列中的某個promise成功或失敗,返回的promise就會成功或失敗。

  static race(instanceList) {
    return new NewPromise((resolve, reject) => {
      if (instanceList.length === 0) {
        resolve([])
        return
      }
      instanceList.forEach(item => {
        // 由於例項列表中的每個元素可能是各種各樣的,所以要用this.resolve方法包裝一層
        this.resolve(item).then(res => {
          // 一旦有一個resolve了,那麼新返回的promise狀態就被resolve
          resolve(res)
        }, error => {
          reject(error)
        })
      })
    })
  }
複製程式碼

完整程式碼

到此為止就實現了一個相對完整的promise,程式碼如下:

class NewPromise {
  constructor(handler) {
    this.state = PENDING
    this.value = undefined
    this.successCallback = []
    this.failureCallback = []
    try {
      handler(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      // 執行器出現錯誤需要reject
      this.reject(e)
    }
  }

  resolve(value) {
    if (this.state !== PENDING) return
    this.state = FULFILLED
    this.value = value
    // 規範中要求then中註冊的回撥以非同步方式執行,保證在resolve執行所有的回撥之前,
    // 所有回撥已經通過then註冊完成
    setTimeout(() => {
      this.successCallback.forEach(item => {
        item(value)
      })
    })
  }
  reject(reason) {
    if (this.state !== PENDING) return
    this.state = REJECTED
    this.value = reason
    setTimeout(() => {
      this.failureCallback.forEach(item => {
        item(reason)
      })
    })
  }
  then(onFulfilled, onRejected) {
    const { state, value } = this
    return new NewPromise((resolveNext, rejectNext) => {
      const resolveNewPromise = value => {
        try {
          // 正常情況
          if (typeof onFulfilled !== 'function') {
            // 不是函式,直接忽略,將then所屬的promise作為then返回的promise的值resolve來做到值的傳遞
            resolveNext(value)
          } else {
            // 獲取then函式回撥的執行結果
            const res = onFulfilled(value)
            if (res instanceof NewPromise) {
              // 當執行結果返回的是一個promise例項,等待這個promise狀態改變後再改變then返回的promise的狀態
              res.then(resolveNext, rejectNext)
            } else {
              // 當返回值是普通值,將其作為新promise的值resolve
              resolveNext(res)
            }
          }
        } catch (e) {
          // 出現異常,新promise的狀態變為rejected,reason就是錯誤物件
          rejectNext(e)
        }
      }
      const rejectNewPromise = reason => {
        try {
          // 正常情況
          if (typeof onRejected !== 'function') {
            // 不是函式,直接忽略,將then所屬的promise作為then返回的promise的值reject來做到值的傳遞
            rejectNext(reason)
          } else {
            // 獲取then函式回撥的執行結果
            const res = onRejected(reason)
            if (res instanceof NewPromise) {
              // 當執行結果返回的是一個promise例項,等待這個promise狀態改變後再改變then返回的promise的狀態
              res.then(resolveNext, rejectNext)
            } else {
              // 當返回值是普通值,將其作為新promise的值reject
              rejectNext(res)
            }
          }
        } catch (e) {
          // 出現異常,新promise的狀態變為rejected,reason就是錯誤物件
          rejectNext(e)
        }
      }
      if (state === PENDING) {
        this.successCallback.push(resolveNewPromise)
        this.failureCallback.push(rejectNewPromise)
      }
      // 要保證在當前promise狀態改變之後,再去改變新的promise的狀態
      if (state === FULFILLED) {
        resolveNewPromise(value)
      }
      if (state === REJECTED) {
        rejectNewPromise(value)
      }
    })
  }
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
  finally(callback) {
    // 返回值是promise物件,回撥在then中執行,也就符合了promise結束後呼叫的原則
    return this.then(
      // then方法的onFulfiled 和 onRejected都會被傳入,保證無論resolved或rejected都會被執行

      // 獲取到promise執行成功的結果,將這個結果作為finally返回的新的promise的值
      res => NewPromise.resolve(callback())
          .then(() => {
            return res
          }),
      // 獲取執行失敗的結果。原理同上
      error => NewPromise.resolve(callback())
          .then(() => {
            throw error
          })
    )
  }
  static allSettled(instanceList) {
    return new NewPromise((resolve, reject) => {
      const results = []
      let count = 0
      if (instanceList.length === 0) {
        resolve([])
        return
      }
      // 定義一個函式,來生成結果陣列
      const generateResult = (result, i) => {
        count++
        results[i] = result
        // 一旦全部執行完成,resolve新返回的promise
        if (count === instanceList.length) {
          resolve(results)
        }
      }
      instanceList.forEach((item, index) => {
        // 在每個promise完成後將狀態記錄到結果陣列中
        this.resolve(item).then(
          value => {
            generateResult({
              status: FULFILLED,
              value
            }, index)
          },
          reason => {
            generateResult({
              status: REJECTED,
              reason
            }, index)
          }
        )
      })
    })
  }
  static resolve(value) {
    // value不存在,直接返回一個resolved狀態的promise
    if (!value) {
      return new NewPromise(function (resolve) {
        resolve()
      })
    }
    // value是promise例項,直接返回
    // 在這裡需要首先判斷是否是promise例項,再進行下邊的判斷
    // 因為我們自己構造的promise也是是object,也有then方法
    if (value instanceof NewPromise) {
      return value
    }
    // 是thenable物件,返回的新的promise例項需要在value狀態改變後再改變,且狀態跟隨value的狀態
    if (typeof value === 'object' && typeof value.then === 'function') {
      return new NewPromise((resolve, reject) => {
        value.then(resolve, reject)
      })
    }
    // value是普通值,返回新的promise並resolve這個普通值
    return new NewPromise(resolve => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new NewPromise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(instanceList) {
    return new NewPromise((resolve, reject) => {
      // 定義存放結果的陣列
      const results = []
      let count = 0
      if (instanceList.length === 0) {
        resolve(results)
        return
      }
      instanceList.forEach((item, index) => {
        // 由於例項列表中的每個元素可能是各種各樣的,所以要用this.resolve方法包裝一層
        this.resolve(item).then(res => {
          results[index] = res
          count++
          // 當都執行完,resolve新返回的promise
          if (count === instanceList.length) {
            resolve(results)
          }
        }, error => {
          // 一旦有一個出錯,就reject新返回的promise
          reject(error)
        })
      })
    })
  }
  static race(instanceList) {
    return new NewPromise((resolve, reject) => {
      if (instanceList.length === 0) {
        resolve([])
        return
      }
      instanceList.forEach(item => {
        // 由於例項列表中的每個元素可能是各種各樣的,所以要用this.resolve方法包裝一層
        this.resolve(item).then(res => {
          // 一旦有一個resolve了,那麼新返回的promise狀態就被resolve
          resolve(res)
        }, error => {
          reject(error)
        })
      })
    })
  }
}
複製程式碼

片尾廣告

想看我寫的更多技術文章可以關注公眾號:一口一個前端

qrcode.jpg

相關文章