亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

萬字詳解JavaScript手寫一個Promise

3403771864 / 436人閱讀

  在面試中,Promise會被經(jīng)常問,下面為大家整合關(guān)于Promise各種問題。

  Promise核心原理實現(xiàn)

  用Promise來寫代碼

  Promise的使用分析

  Promise是一個在執(zhí)行的時候,需要傳遞一個執(zhí)行器(回調(diào)函數(shù))進去,執(zhí)行器會立即執(zhí)行的一一個類。

  Promise中的狀態(tài)分為三個,分別是:

  pending→等待

  fulfilled→成功

  rejected→失敗

  狀態(tài)的切換只有兩種,分別是:

  pending→fulfilled

  pending→rejected

  須知:當(dāng)狀態(tài)發(fā)生改變時就不會再次改變:

  執(zhí)行器中的兩個參數(shù),分別是resolve和reject,這兩個回調(diào)函數(shù),調(diào)用resolve是從pending狀態(tài)到fulfilled,調(diào)用reject是從狀態(tài)pending到rejected。傳遞給這兩個回調(diào)函數(shù)的參數(shù)會作為成功或失敗的值。

  Promise的實例對象具有一個then方法,該方法接受兩個回調(diào)函數(shù),分別來處理成功與失敗的狀態(tài),then方法內(nèi)部會進行判斷,然后根據(jù)當(dāng)前狀態(tài)確定調(diào)用的回調(diào)函數(shù)。then方法應(yīng)該是被定義在原型對象中的。

  then的回調(diào)函數(shù)有一個值,當(dāng)達到這個值時候,表示成功后返回的值;當(dāng)返回不是這值時,就表示失敗。

  MyPromise的實現(xiàn)

  根據(jù)我們上面的分析,寫出如下代碼:

  MyPromise.js
  // 定義所有狀態(tài)的常量
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  // Promise實質(zhì)上就是一個類,首先創(chuàng)建一個Promise的類
  class MyPromise {
  // 實例化Promise時需要一個回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
  constructor(executor) {
  // 在調(diào)用executor需要傳遞兩個回調(diào)函數(shù),分別是resolve和reject
  executor(this.resolve, this.reject)
  }
  // Promise 的狀態(tài)
  status = PENDING
  // 記錄成功與失敗的值
  value = undefined
  reason = undefined
  resolve = (value) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向為該實例化后的對象
  // 形參value表示,調(diào)用resolve時傳遞的參數(shù)
  // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
  if (this.status !== PENDING) return
  // 將狀態(tài)修改為成功
  this.status = FULFILLED
  // 將傳入的值進行保存
  this.value = value
  }
  reject = (reason) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向為該實例化后的對象
  // 形參reason表示,調(diào)用reject時傳遞的失敗的原因
  // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
  if (this.status !== PENDING) return
  // 將狀態(tài)修改為失敗
  this.status = REJECTED
  // 保存失敗的原因
  this.reason = reason
  }
  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 將成功的值作為參數(shù)返回
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  // 將失敗的原因作為參數(shù)返回
  onRejected(this.reason)
  }
  }
  }
  // 導(dǎo)出Promise
  module.exports = MyPromise

  現(xiàn)在我們就來寫一段代碼驗證一下上面的代碼

  驗證resolve:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 輸出
  成功
  */

  驗證reject:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  reject('失敗')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 輸出
  失敗
  */

  驗證狀態(tài)不可變:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  reject('失敗')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 輸出
  成功
  */

  在Promise中加入異步操作

  當(dāng)代碼中存在異步操作時,表示Promise將毫無用處,

  例如下面這段代碼:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  setTimeout(resolve, 2000, '成功')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })

  這段代碼2000ms后沒有任何輸出,為了解決這個問題我們將對自己編寫的類進行如下操作:

  創(chuàng)建兩個實例方法用于存儲我們傳入的成功與失敗的處理邏輯。

  為then方法添加狀態(tài)為pending時的處理邏輯,這時將傳遞進來的屬性保存到實例上。

  在成功或者失敗時調(diào)用相應(yīng)的傳遞進來的回調(diào)函數(shù)(實例屬性存在函數(shù)的情況下)。

  實現(xiàn)代碼如下:

  // MyPromise.js
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  class MyPromise {
  constructor(executor) {
  executor(this.resolve, this.reject)
  }
  status = PENDING
  value = undefined
  reason = undefined
  // 存儲成功與失敗的處理邏輯
  onFulfilled = undefined
  onRejected = undefined
  resolve = (value) => {
  if (this.status !== PENDING) return
  this.status = FULFILLED
  this.value = value
  // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
  this.onFulfilled && this.onFulfilled(this.value)
  }
  reject = (reason) => {
  if (this.status !== PENDING) return
  this.status = REJECTED
  this.reason = reason
  // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
  this.onRejected && this.onRejected(this.reason)
  }
  then (onFulfilled, onRejected) {
  if (this.status === FULFILLED) {
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  this.onFulfilled = onFulfilled
  this.onRejected = onRejected
  }
  }
  }
  module.exports = MyPromise

  這里的this.onFulfilled && this.onFulfilled(this.value)表示如果該屬性存在就調(diào)用這個方法。

  現(xiàn)在我們重新執(zhí)行一開始上面那一段代碼,2s后會成功輸出成功。

  實現(xiàn)then方法的多次調(diào)用

  Promise實例中存在要給then方法,允許我們在Promise實例中鏈式調(diào)用,每個then方法還會返回一個Promise實例,

  如下圖所示:

1.png

  Promise實例方法then是可以多次進行調(diào)用,而我們現(xiàn)在自己封裝的卻執(zhí)行調(diào)用一次,現(xiàn)在根據(jù)新需要來重新改寫我們的代碼,

  實現(xiàn)思路如下:

  定義可以存儲多個回調(diào)的數(shù)組,用于存儲多個回調(diào)函數(shù)。

  如果是同步執(zhí)行的代碼,執(zhí)行后立即知道執(zhí)行結(jié)果,所以可以直接調(diào)用回調(diào)函數(shù)。

  如果是異步代碼,需要將每次回調(diào)函數(shù)保存到數(shù)組中,然后狀態(tài)變化時依次調(diào)用函數(shù)。

  實現(xiàn)代碼如下:

  // MyPromise.js
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  class MyPromise {
  constructor(executor) {
  executor(this.resolve, this.reject)
  }
  status = PENDING
  value = undefined
  reason = undefined
  // 存儲成功與失敗的處理邏輯
  onFulfilled = []
  onRejected = []
  resolve = (value) => {
  if (this.status !== PENDING) return
  this.status = FULFILLED
  this.value = value
  // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
  while (this.onFulfilled.length) {
  // Array.prototype.shift() 用于刪除數(shù)組第一個元素,并返回
  this.onFulfilled.shift()(this.value)
  }
  }
  reject = (reason) => {
  if (this.status !== PENDING) return
  this.status = REJECTED
  this.reason = reason
  // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
  while (this.onRejected.length) {
  // Array.prototype.shift() 用于刪除數(shù)組第一個元素,并返回
  this.onRejected.shift()(this.reason)
  }
  }
  then (onFulfilled, onRejected) {
  if (this.status === FULFILLED) {
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  }
  }
  module.exports = MyPromise

  這里我們通過數(shù)組的shift()方法,該方法刪除數(shù)組的第一個元素,并返回,返回的這個值正好是一個回調(diào)函數(shù),然后調(diào)用該函數(shù)即可實現(xiàn)該功能。

  實現(xiàn)then的鏈式調(diào)用

  想要實現(xiàn)then的鏈式調(diào)用,主要解決兩個問題:

  返回的是一個新的MyPormise實例。

  then的返回值作為下一次的鏈式調(diào)用的參數(shù)。

  這里分為兩種情況:

  直接返回一個值,可以直接作為值使用

  返回一個新的MyPormise實例,此時就需要判斷其狀態(tài)

  實現(xiàn)代碼如下:

  // MyPromise.js
  /* 省略的代碼同上 */
  class MyPromise {
  /* 省略的代碼同上 */
  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // then 方法返回一個MyPromise實例
  return new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 將成功的值作為參數(shù)返回
  // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
  const result = onFulfilled(this.value)
  // 如果返回的是一個普通的值,直接調(diào)用resolve
  // 如果是一個MyPromise實例,根據(jù)返回的解決來決定是調(diào)用resolve,還是reject
  resolvePromise(result, resolve, reject)
  } else if (this.status === REJECTED) {
  // 將失敗的原因作為參數(shù)返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  }
  }
  function resolvePromise (result, resolve, reject) {
  // 判斷傳遞的result是不是MyPromise的實例對象
  if (result instanceof MyPromise) {
  // 說明是MyPromise的實例對象
  // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
  // result.then(value => resolve(value), reason => reject(reason))
  // 簡寫
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

  then方法鏈式調(diào)用識別Promise對象自返回

  在Promise中,如果then方法返回的是自己的promise對象,則會發(fā)生promise的嵌套,這個時候程序會報錯,

  測試代碼如下:

  var promise = new Promise((resolve, reject) => {
  resolve(100)
  })
  var p1 = promise.then(value => {
  console.log(value)
  return p1
  })
  // 100
  // Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

  想要解決這個問題其實我們只需要在then方法返回的MyPromise實例對象與then中回調(diào)函數(shù)返回的值進行比對,如果相同的返回一個reject的MyPromise實例對象,并創(chuàng)建一個TypeError類型的Error。

  實現(xiàn)代碼如下:

  // MyPromise.js
  /* 省略的代碼同上 */
  then (onFulfilled, onRejected) {
  // then 方法返回一個MyPromise實例
  const promise = new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 這里并不需要延遲執(zhí)行,而是通過setTimeout將其變成異步函數(shù)
  // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
  setTimeout(() => {
  // 將成功的值作為參數(shù)返回
  // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
  const result = onFulfilled(this.value)
  // 如果返回的是一個普通的值,直接調(diào)用resolve
  // 如果是一個MyPromise實例,根據(jù)返回的解決來決定是調(diào)用resolve,還是reject
  resolvePromise(promise, result, resolve, reject)
  }, 0)
  } else if (this.status === REJECTED) {
  // 將失敗的原因作為參數(shù)返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  return promise
  }
  }
  function resolvePromise (promise, result, resolve, reject) {
  // 這里修改一下該函數(shù),如果return的Promise實例對象,也就是傳入的promise===result的話,說明在promise中return的是當(dāng)前promise對象。
  if (promise === result) {
  // 這里調(diào)用reject,并拋出一個Error
  // return 是必須的,阻止程序向下執(zhí)行
  return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判斷傳遞的result是不是MyPromise的實例對象
  if (result instanceof MyPromise) {
  // 說明是MyPromise的實例對象
  // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
  // result.then(value => resolve(value), reason => reject(reason))
  // 簡寫
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

  這里then方法中的setTimeout的作用并不是延遲執(zhí)行,而是為了調(diào)用resolvePromise函數(shù)時,保證創(chuàng)建的promise存在。

  捕獲錯誤及 then 鏈式調(diào)用其他狀態(tài)代碼補充

  Promise異常想在還未處理,為了保證代碼的穩(wěn)定性,需要做處理。

  捕獲執(zhí)行器錯誤

  現(xiàn)在我們需要對執(zhí)行器進行異常捕獲,如果發(fā)生異常,就將我們的狀態(tài)修改為rejected。

  捕獲執(zhí)行器的錯誤也比較簡單,只需要在構(gòu)造函數(shù)中加入try...catch語句就可以,

  實現(xiàn)代碼如下:

  constructor(executor) {
  try {
  // 在調(diào)用executor需要傳遞兩個回調(diào)函數(shù),分別是resolve和reject
  executor(this.resolve, this.reject)
  } catch (e) {
  // 發(fā)生異常調(diào)用reject
  this.reject(e)
  }
  }

  測試代碼如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  throw new Error('執(zhí)行器錯誤')
  })
  promise.then(value => {
  console.log(value);
  }, error => {
  console.log(error.message);
  })
  /* 輸出
  執(zhí)行器錯誤
  */

  捕獲then中的報錯

  現(xiàn)在我們需要對then中的異常捕獲到,并在下一次鏈式調(diào)用中傳遞到then的第二個函數(shù)中,實現(xiàn)的方式也是通過try...catch語句,

  示例代碼如下:

  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // then 方法返回一個MyPromise實例
  const promise = new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 這里并不需要延遲執(zhí)行,而是通過setTimeout將其變成異步函數(shù)
  // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
  setTimeout(() => {
  try {
  // 將成功的值作為參數(shù)返回
  // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
  const result = onFulfilled(this.value)
  // 如果返回的是一個普通的值,直接調(diào)用resolve
  // 如果是一個MyPromise實例,根據(jù)返回的解決來決定是調(diào)用resolve,還是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 將失敗的原因作為參數(shù)返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  return promise
  }

  測試代碼如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  })
  // 第一個then方法中的錯誤要在第二個then方法中捕獲到
  promise.then(value => {
  console.log('resolve', value)
  throw new Error('then的執(zhí)行過程中遇到異常')
  }).then(null, reason => {
  console.log(reason.message)
  })
  /* 輸出
  resolve 成功
  then的執(zhí)行過程中遇到異常
  */

  錯誤與異步狀態(tài)的鏈式調(diào)用

  現(xiàn)在只對成功狀態(tài)的then進行的鏈式調(diào)用以及錯誤處理,錯誤與異步狀態(tài)未進行處理,其實處理起來也是一樣的,

  示例代碼如下:

  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // then 方法返回一個MyPromise實例
  const promise = new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 這里并不需要延遲執(zhí)行,而是通過setTimeout將其變成異步函數(shù)
  // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
  setTimeout(() => {
  try {
  // 將成功的值作為參數(shù)返回
  // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
  const result = onFulfilled(this.value)
  // 如果返回的是一個普通的值,直接調(diào)用resolve
  // 如果是一個MyPromise實例,根據(jù)返回的解決來決定是調(diào)用resolve,還是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
  setTimeout(() => {
  try {
  const result = onRejected(this.reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else {
  this.onFulfilled.push((value) => {
  setTimeout(() => {
  try {
  const result = onFulfilled(value)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  this.onRejected.push((reason) => {
  setTimeout(() => {
  try {
  const result = onRejected(reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  }
  })
  return promise
  }

  測試代碼如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  setTimeout(resolve, 2000, '成功')
  })
  // 第一個then方法中的錯誤要在第二個then方法中捕獲到
  promise.then(value => {
  console.log('resolve', value)
  throw new Error('then的執(zhí)行過程中遇到異常')
  }).then(null, reason => {
  console.log(reason.message)
  })
  /* 輸出
  resolve 成功
  then的執(zhí)行過程中遇到異常
  */

  將then方法的參數(shù)變成可選參數(shù)

  Promise中的then方法其實是兩個可以可選參數(shù),如果我們不傳遞任何參數(shù)的話,里面的結(jié)果是向下傳遞的,直到捕獲為止,

  例如下面這段代碼:

  new Promise((resolve, reject) => {
  resolve(100)
  })
  .then()
  .then()
  .then()
  .then(value => console.log(value))
  // 最后一個then輸入100

  這段代碼可以理解為:

  new Promise((resolve, reject) => {
  resolve(100)
  })
  .then(value => value)
  .then(value => value)
  .then(value => value)
  .then(value => console.log(value))

  所以說我們只需要在沒有傳遞回調(diào)函數(shù)時,賦值一個默認的回調(diào)函數(shù)即可。

  實現(xiàn)代碼如下:

  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // 如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個默認值,用于參數(shù)傳遞
  onFulfilled = onFulfilled ? onFulfilled : value => value
  // 同理
  onRejected = onRejected ? onRejected : reason => { throw reason }
  // then 方法返回一個MyPromise實例
  const promise = new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {...
  } else if (this.status === REJECTED) {...
  } else {...
  }
  })
  return promise
  }

  Promise.all方法的實現(xiàn)

  關(guān)于all()方法的使用,可以參數(shù)Promise.all()。簡單的說Promise.all()會將多個Promise實例包裝為一個Promise實例,且順序與調(diào)用順序一致,

  示例代碼如下:

  function p1 () {
  return new Promise((resolve, reject) => {
  setTimeout(() => {
  resolve('p1')
  }, 2000)
  })
  }
  function p2 () {
  return new Promise((resolve, reject) => {
  setTimeout(() => {
  resolve('p2')
  }, 0)
  })
  }
  Promise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
  console.log(result)
  // ["a", "b", "p1", "p2", "c"]
  })

  在這段代碼中,我們的p1的執(zhí)行是延遲了2s的,這里如果不使用Promise.all()的話最終順序是與我們調(diào)用不同的。

  現(xiàn)在我們來分析一下all()的實現(xiàn)思路:

  all()方法可以通過類直接調(diào)用,所以是一個靜態(tài)方法

  all()方法接收一個數(shù)組,數(shù)組中的值可以是一個普通值,也可以是一個MyPromise的實例對象

  return一個新的MyPromise實例對象

  遍歷數(shù)組中的每一個值,判斷值得類型,如果是一個普通值得話直接將值存入一個數(shù)組;如果是一個MyPromise的實例對象的話,會調(diào)用then方法,然后根據(jù)執(zhí)行后的狀態(tài),如果失敗的話調(diào)用新的MyPromise實例對象中的rejecte,如果是成功話將這個值存入一個數(shù)組

  存入數(shù)組時計數(shù),如果存入的數(shù)量達到傳入的數(shù)組長度,說明調(diào)用完畢,執(zhí)行resolve并將最終的結(jié)果數(shù)組作為參數(shù)返回。

  實現(xiàn)代碼:

  /**
  * @description: 將多個Promise實例合并為一個Promise實例
  * @param {*} array Promise或者普通值
  * @returns {Promise}
  */
  static all (array) {
  // 用于存放最終結(jié)果的數(shù)組
  let result = []
  // 用于計算當(dāng)前已經(jīng)執(zhí)行完的實例的數(shù)量
  let count = 0
  // 最后返回的是一個Promise實例
  return new MyPromise((resolve, reject) => {
  /**
  * @description: 將執(zhí)行完畢的值加入結(jié)果數(shù)組,并根據(jù)實際情況決定是否調(diào)用resolve
  * @param {*} result 存放結(jié)果的數(shù)組
  * @param {*} index 要加入的索引
  * @param {*} value 要加入數(shù)組的值
  * @param {*} resolve Promise中的resolve
  */
  function addResult (result, index, value, resolve) {
  // 根據(jù)索引值,將結(jié)果堆入數(shù)組中
  result[index] = value
  // 執(zhí)行完畢一個 count+1,如果當(dāng)前值等于總長度的話說明已經(jīng)執(zhí)行結(jié)束了,可以直接調(diào)用resolve,說明已經(jīng)成功執(zhí)行完畢了
  if (++count === array.length) {
  // 將執(zhí)行結(jié)果返回
  resolve(result)
  }
  }
  // 遍歷穿入的數(shù)組,每個都執(zhí)行then方法,獲取到最終的結(jié)果
  array.forEach((p, index) => {
  // 判斷p是不是MyPromise的實例,如果是的話調(diào)用then方法,不是直接將值加入數(shù)組中
  if (p instanceof MyPromise) {
  p.then(
  // 成功時將結(jié)果直接加入數(shù)組中
  value => {
  addResult(result, index, value, resolve)
  },
  // 如果失敗直接返回失敗原因
  reason => {
  reject(reason)
  }
  )
  }
  else {
  addResult(result, index, p, resolve)
  }
  })
  })
  }
  Promise.resolve方法的實現(xiàn)

  關(guān)于Promise.resolve()方法的用法可以參考Promise.resolve()與Promise.reject()。

  我們實現(xiàn)的思路主要如下:

  該方法是一個靜態(tài)方法

  該方法接受的如果是一個值就將該值包裝為一個MyPromise的實例對象返回,如果是一個MyPromise的實例對象,調(diào)用then方法返回。

  實現(xiàn)代碼如下:

  static resolve (value) {
  // 如果是MyPromise的實例,就直接返回這個實例
  if (value instanceof MyPromise) return value
  // 如果不是的話創(chuàng)建一個MyPromise實例,并返回傳遞的值
  return new MyPromise((resolve) => {
  resolve(value)
  })
  }

  finally方法的實現(xiàn)

  關(guān)于finally方法可參考finally(),實現(xiàn)該方法的實現(xiàn)代碼如下:

  finally (callback) {
  // 如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
  // 而且then方法就是返回一個promise對象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
  // 我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把value也return
  // 失敗的回調(diào)也拋出原因
  // 如果callback是一個異步的promise對象,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
  return this.then(value => {
  // 把callback調(diào)用之后返回的promise傳遞過去,并且執(zhí)行promise,且在成功之后返回value
  return MyPromise.resolve(callback()).then(() => value)
  }, reason => {
  // 失敗之后調(diào)用的then方法,然后把失敗的原因返回出去。
  return MyPromise.resolve(callback()).then(() => { throw reason })
  })
  }

  測試:

  function p1 () {
  return new MyPromise((resolve, reject) => {
  setTimeout(() => {
  resolve('p1')
  }, 2000)
  })
  }
  function p2 () {
  return new MyPromise((resolve, reject) => {
  reject('p2 reject')
  })
  }
  p2().finally(
  () => {
  console.log('finally p2')
  return p1()
  }
  ).then(
  value => {
  console.log(value)
  }, reason => {
  console.log(reason)
  }
  )
  // finally p2
  // 兩秒之后執(zhí)行p2 reject

  catch方法的實現(xiàn)

  關(guān)于catch方法可以參考catch(),實現(xiàn)該方法其實非常簡單,只需要在內(nèi)部調(diào)用then方法,不傳遞第一個回調(diào)函數(shù)即可,

  實現(xiàn)代碼如下:

  catch (callback) {
  return this.then(null, failCallback)
  }

  測試如下:

  function p () {
  return new MyPromise((resolve, reject) => {
  reject(new Error('reject'))
  })
  }
  p()
  .then(value => {
  console.log(value)
  })
  .catch(reason => console.log(reason))

  完整代碼

  // MyPromise.js
  // 定義所有狀態(tài)的常量
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  // Promise實質(zhì)上就是一個類,首先創(chuàng)建一個Promise的類
  class MyPromise {
  // 實例化Promise時需要一個回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
  constructor(executor) {
  try {
  // 在調(diào)用executor需要傳遞兩個回調(diào)函數(shù),分別是resolve和reject
  executor(this.resolve, this.reject)
  } catch (e) {
  // 發(fā)生異常調(diào)用reject
  this.reject(e)
  }
  }
  // Promise 的狀態(tài)
  status = PENDING
  // 記錄成功與失敗的值
  value = undefined
  reason = undefined
  // 存儲成功與失敗的處理邏輯
  onFulfilled = []
  onRejected = []
  resolve = (value) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向為該實例化后的對象
  // 形參value表示,調(diào)用resolve時傳遞的參數(shù)
  // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
  if (this.status !== PENDING) return
  // 將狀態(tài)修改為成功
  this.status = FULFILLED
  // 將傳入的值進行保存
  this.value = value
  // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
  // this.onFulfilled && this.onFulfilled(this.value)
  while (this.onFulfilled.length) {
  // Array.prototype.shift() 用于刪除數(shù)組第一個元素,并返回
  this.onFulfilled.shift()(this.value)
  }
  }
  reject = (reason) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向為該實例化后的對象
  // 形參reason表示,調(diào)用reject時傳遞的失敗的原因
  // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
  if (this.status !== PENDING) return
  // 將狀態(tài)修改為失敗
  this.status = REJECTED
  // 保存失敗的原因
  this.reason = reason
  // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
  // this.onRejected && this.onRejected(this.reason)
  while (this.onRejected.length) {
  // Array.prototype.shift() 用于刪除數(shù)組第一個元素,并返回
  this.onRejected.shift()(this.reason)
  }
  }
  // then方法的實現(xiàn)
  then (onFulfilled, onRejected) {
  // 如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個默認值,用于參數(shù)傳遞
  onFulfilled = onFulfilled ? onFulfilled : value => value
  // 同理
  onRejected = onRejected ? onRejected : reason => { throw reason }
  // then 方法返回一個MyPromise實例
  const promise = new MyPromise((resolve, reject) => {
  // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
  if (this.status === FULFILLED) {
  // 這里并不需要延遲執(zhí)行,而是通過setTimeout將其變成異步函數(shù)
  // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
  setTimeout(() => {
  try {
  // 將成功的值作為參數(shù)返回
  // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
  const result = onFulfilled(this.value)
  // 如果返回的是一個普通的值,直接調(diào)用resolve
  // 如果是一個MyPromise實例,根據(jù)返回的解決來決定是調(diào)用resolve,還是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
  setTimeout(() => {
  try {
  const result = onRejected(this.reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else {
  // 表示既不是成功,也不是失敗。這個時候保存?zhèn)鬟f進來的兩個回調(diào)
  // this.onFulfilled.push(onFulfilled)
  // this.onRejected.push(onRejected)
  this.onFulfilled.push((value) => {
  setTimeout(() => {
  try {
  const result = onFulfilled(value)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  this.onRejected.push((reason) => {
  setTimeout(() => {
  try {
  const result = onRejected(reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  }
  })
  return promise
  }
  catch (callback) {
  return this.then(null, callback)
  }
  finally (callback) {
  // 如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
  // 而且then方法就是返回一個promise對象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
  // 我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把value也return
  // 失敗的回調(diào)也拋出原因
  // 如果callback是一個異步的promise對象,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
  return this.then(value => {
  // 把callback調(diào)用之后返回的promise傳遞過去,并且執(zhí)行promise,且在成功之后返回value
  return MyPromise.resolve(callback()).then(() => value)
  }, reason => {
  // 失敗之后調(diào)用的then方法,然后把失敗的原因返回出去。
  return MyPromise.resolve(callback()).then(() => { throw reason })
  })
  }
  /**
  * @description: 將多個Promise實例合并為一個Promise實例
  * @param {*} array Promise或者普通值
  * @returns {Promise}
  */
  static all (array) {
  // 用于存放最終結(jié)果的數(shù)組
  let result = []
  // 用于計算當(dāng)前已經(jīng)執(zhí)行完的實例的數(shù)量
  let count = 0
  // 最后返回的是一個Promise實例
  return new MyPromise((resolve, reject) => {
  /**
  * @description: 將執(zhí)行完畢的值加入結(jié)果數(shù)組,并根據(jù)實際情況決定是否調(diào)用resolve
  * @param {*} result 存放結(jié)果的數(shù)組
  * @param {*} index 要加入的索引
  * @param {*} value 要加入數(shù)組的值
  * @param {*} resolve Promise中的resolve
  */
  function addResult (result, index, value, resolve) {
  // 根據(jù)索引值,將結(jié)果堆入數(shù)組中
  result[index] = value
  // 執(zhí)行完畢一個 count+1,如果當(dāng)前值等于總長度的話說明已經(jīng)執(zhí)行結(jié)束了,可以直接調(diào)用resolve,說明已經(jīng)成功執(zhí)行完畢了
  if (++count === array.length) {
  // 將執(zhí)行結(jié)果返回
  resolve(result)
  }
  }
  // 遍歷穿入的數(shù)組,每個都執(zhí)行then方法,獲取到最終的結(jié)果
  array.forEach((p, index) => {
  // 判斷p是不是MyPromise的實例,如果是的話調(diào)用then方法,不是直接將值加入數(shù)組中
  if (p instanceof MyPromise) {
  p.then(
  // 成功時將結(jié)果直接加入數(shù)組中
  value => {
  addResult(result, index, value, resolve)
  },
  // 如果失敗直接返回失敗原因
  reason => {
  reject(reason)
  }
  )
  }
  else {
  addResult(result, index, p, resolve)
  }
  })
  })
  }
  static resolve (value) {
  // 如果是MyPromise的實例,就直接返回這個實例
  if (value instanceof MyPromise) return value
  // 如果不是的話創(chuàng)建一個MyPromise實例,并返回傳遞的值
  return new MyPromise((resolve) => {
  resolve(value)
  })
  }
  }
  function resolvePromise (promise, result, resolve, reject) {
  // 這里修改一下該函數(shù),如果return的Promise實例對象,也就是傳入的promise===result的話,說明在promise中return的是當(dāng)前promise對象。
  if (promise === result) {
  // 這里調(diào)用reject,并拋出一個Error
  // return 是必須的,阻止程序向下執(zhí)行
  return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判斷傳遞的result是不是MyPromise的實例對象
  if (result instanceof MyPromise) {
  // 說明是MyPromise的實例對象
  // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
  // result.then(value => resolve(value), reason => reject(reason))
  // 簡寫
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

      上面內(nèi)容很多,大家可以慢慢消化。


文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/127765.html

相關(guān)文章

  • 馬蹄疾 | 詳解 JavaScript 異步機制及發(fā)展歷程(萬字長文)

    摘要:本文從入手,系統(tǒng)的回顧的異步機制及發(fā)展歷程。需要提醒的是,文本沒有討論的異步機制。本文是專題系列文章之一,后續(xù)會有更多專題推出地址持續(xù)更新博客地址文章排版真的很漂亮如果覺得對你有幫助,歡迎來點或者來我的博客親口告訴我本文從Event Loop、Promise、Generator、async await入手,系統(tǒng)的回顧 JavaScript 的異步機制及發(fā)展歷程。 需要提醒的是,文本沒有討論 ...

    KoreyLee 評論0 收藏0
  • 馬蹄疾 | 詳解 JavaScript 異步機制及發(fā)展歷程(萬字長文)

    摘要:本文從入手,系統(tǒng)的回顧的異步機制及發(fā)展歷程。需要提醒的是,文本沒有討論的異步機制。這就是之前提到的事件觸發(fā)線程。其實無論是請求還是定時器還是事件,我們都可以統(tǒng)稱它們?yōu)槭录?。第二階段,引擎線程專注于處理事件。將外元素的事件回調(diào)放入調(diào)用棧。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....

    shaonbean 評論0 收藏0
  • 22道JavaScript高頻手寫面試題

    JavaScript筆試部分 點擊關(guān)注本公眾號獲取文檔最新更新,并可以領(lǐng)取配套于本指南的 《前端面試手冊》 以及最標準的簡歷模板. 實現(xiàn)防抖函數(shù)(debounce) 防抖函數(shù)原理:在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。 那么與節(jié)流函數(shù)的區(qū)別直接看這個動畫實現(xiàn)即可。 showImg(https://segmentfault.com/img/remote/146000002...

    Alan 評論0 收藏0
  • 2019-我的前端面試題

    摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關(guān)問題,其他公司壓根沒問。我自己回答的是自己開發(fā)組件面臨的問題。完全不用擔(dān)心對方到時候打電話核對的問題。 2019的5月9號,離發(fā)工資還有1天的時候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經(jīng)是第2個月沒工資了。 公...

    iKcamp 評論0 收藏0
  • 前端進階資源整理

    摘要:前端進階進階構(gòu)建項目一配置最佳實踐狀態(tài)管理之痛點分析與改良開發(fā)中所謂狀態(tài)淺析從時間旅行的烏托邦,看狀態(tài)管理的設(shè)計誤區(qū)使用更好地處理數(shù)據(jù)愛彼迎房源詳情頁中的性能優(yōu)化從零開始,在中構(gòu)建時間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個故事講好和 前端進階 webpack webpack進階構(gòu)建項目(一) Webpack 4 配置最佳實踐 react Redux狀態(tài)管理之痛點、分析與...

    BlackMass 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<