<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
手寫Promise現在已經成了面試的熱門內容,但在實際開發中基本都不會去手寫一個Promise,但是在面試中各種手寫題可能就會遇到一個手寫Promise,我們可以儘量提高我們的上限,從而獲取更多的工作機會。
首先我們從使用的角度來分析一下Promise,然後編寫一個最簡單版本的Promise。
Promise就是一個類,在執行這個類的時候,需要傳遞一個執行器(回撥函數)進去,執行器會立即執行。
Promise中的狀態分為三個,分別是:
狀態的切換隻有兩種,分別是:
一旦狀態發生改變,就不會再次改變:
根據我們上面的分析,寫出如下程式碼:
MyPromise.js // 定義所有狀態的常數 const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' // Promise實質上就是一個類,首先建立一個Promise的類 class MyPromise { // 範例化Promise時需要一個回撥函數,該回撥函數立即執行 constructor(executor) { // 在呼叫executor需要傳遞兩個回撥函數,分別是resolve和reject executor(this.resolve, this.reject) } // Promise 的狀態 status = PENDING // 記錄成功與失敗的值 value = undefined reason = undefined resolve = (value) => {// 這裡使用箭頭函數是為了使其內部的this指向為該範例化後的物件 // 形參value表示,呼叫resolve時傳遞的引數 // 如果當前狀態不是pending,就直接跳出該邏輯 if (this.status !== PENDING) return // 將狀態修改為成功 this.status = FULFILLED // 將傳入的值進行儲存 this.value = value } reject = (reason) => {// 這裡使用箭頭函數是為了使其內部的this指向為該範例化後的物件 // 形參reason表示,呼叫reject時傳遞的失敗的原因 // 如果當前狀態不是pending,就直接跳出該邏輯 if (this.status !== PENDING) return // 將狀態修改為失敗 this.status = REJECTED // 儲存失敗的原因 this.reason = reason } // then方法的實現 then (onFulfilled, onRejected) { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 將成功的值作為引數返回 onFulfilled(this.value) } else if (this.status === REJECTED) { // 將失敗的原因作為引數返回 onRejected(this.reason) } } } // 匯出Promise module.exports = MyPromise
現在我們就來寫一段程式碼驗證一下上面的程式碼
驗證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); }) /* 輸出 失敗 */
驗證狀態不可變:
const MyPromise = require('./myPromise') let promise = new MyPromise((resolve, reject) => { resolve('成功') reject('失敗') }) promise.then(value => { console.log(value); }, reason => { console.log(reason); }) /* 輸出 成功 */
如果我們的程式碼中存在非同步操作,我們自己寫的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後沒有任何輸出,為了解決這個問題我們將對自己編寫的類進行如下操作:
then
方法新增狀態為pending
時的處理邏輯,這時將傳遞進來的屬性儲存到範例上。實現程式碼如下:
// 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 // 如果將狀態修復為成功,呼叫成功的回撥 this.onFulfilled && this.onFulfilled(this.value) } reject = (reason) => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 如果將狀態修復為失敗,呼叫失敗的回撥 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 { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 this.onFulfilled = onFulfilled this.onRejected = onRejected } } } module.exports = MyPromise
這裡的this.onFulfilled && this.onFulfilled(this.value)
表示如果該屬性存在就呼叫這個方法。
現在我們重新執行一開始上面那一段程式碼,2s後會成功輸出成功
。
Promise範例中存在要給then
方法,允許我們在Promise範例中鏈式呼叫,每個then
方法還會返回一個Promise範例,
如下圖所示:
Promise實體方法then
是可以多次進行呼叫,而我們現在自己封裝的卻執行呼叫一次,現在根據新需要來重新改寫我們的程式碼,
實現思路如下:
實現程式碼如下:
// 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 // 如果將狀態修復為成功,呼叫成功的回撥 while (this.onFulfilled.length) { // Array.prototype.shift() 用於刪除陣列第一個元素,並返回 this.onFulfilled.shift()(this.value) } } reject = (reason) => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 如果將狀態修復為失敗,呼叫失敗的回撥 while (this.onRejected.length) { // Array.prototype.shift() 用於刪除陣列第一個元素,並返回 this.onRejected.shift()(this.reason) } } then (onFulfilled, onRejected) { if (this.status === FULFILLED) { onFulfilled(this.value) } else if (this.status === REJECTED) { onRejected(this.reason) } else { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 this.onFulfilled.push(onFulfilled) this.onRejected.push(onRejected) } } } module.exports = MyPromise
這裡我們通過陣列的shift()
方法,該方法刪除陣列的第一個元素,並返回,返回的這個值正好是一個回撥函數,然後呼叫該函數即可實現該功能。
想要實現then的鏈式呼叫,主要解決兩個問題:
MyPormise
範例。then
的返回值作為下一次的鏈式呼叫的引數。這裡分為兩種情況:
MyPormise
範例,此時就需要判斷其狀態實現程式碼如下:
// MyPromise.js /* 省略的程式碼同上 */ class MyPromise { /* 省略的程式碼同上 */ // then方法的實現 then (onFulfilled, onRejected) { // then 方法返回一個MyPromise範例 return new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 將成功的值作為引數返回 // 儲存執行回撥函數的結果 const result = onFulfilled(this.value) // 如果返回的是一個普通的值,直接呼叫resolve // 如果是一個MyPromise範例,根據返回的解決來決定是呼叫resolve,還是reject resolvePromise(result, resolve, reject) } else if (this.status === REJECTED) { // 將失敗的原因作為引數返回 onRejected(this.reason) } else { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 this.onFulfilled.push(onFulfilled) this.onRejected.push(onRejected) } }) } } function resolvePromise (result, resolve, reject) { // 判斷傳遞的result是不是MyPromise的範例物件 if (result instanceof MyPromise) { // 說明是MyPromise的範例物件 // 呼叫.then方法,然後在回撥函數中獲取到具體的值,然後呼叫具體的回撥 // result.then(value => resolve(value), reason => reject(reason)) // 簡寫 result.then(resolve, reject) } else { resolve(result) } } module.exports = MyPromise
在Promise中,如果then
方法返回的是自己的promise
物件,則會發生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中回撥函數返回的值進行比對,如果相同的返回一個reject的MyPromise範例物件,並建立一個TypeError型別的Error。
實現程式碼如下:
// MyPromise.js /* 省略的程式碼同上 */ then (onFulfilled, onRejected) { // then 方法返回一個MyPromise範例 const promise = new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 這裡並不需要延遲執行,而是通過setTimeout將其變成非同步函數 // 如果不變成非同步的話是在函數內獲取不到promise的 setTimeout(() => { // 將成功的值作為引數返回 // 儲存執行回撥函數的結果 const result = onFulfilled(this.value) // 如果返回的是一個普通的值,直接呼叫resolve // 如果是一個MyPromise範例,根據返回的解決來決定是呼叫resolve,還是reject resolvePromise(promise, result, resolve, reject) }, 0) } else if (this.status === REJECTED) { // 將失敗的原因作為引數返回 onRejected(this.reason) } else { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 this.onFulfilled.push(onFulfilled) this.onRejected.push(onRejected) } }) return promise } } function resolvePromise (promise, result, resolve, reject) { // 這裡修改一下該函數,如果return的Promise範例物件,也就是傳入的promise===result的話,說明在promise中return的是當前promise物件。 if (promise === result) { // 這裡呼叫reject,並丟擲一個Error // return 是必須的,阻止程式向下執行 return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // 判斷傳遞的result是不是MyPromise的範例物件 if (result instanceof MyPromise) { // 說明是MyPromise的範例物件 // 呼叫.then方法,然後在回撥函數中獲取到具體的值,然後呼叫具體的回撥 // result.then(value => resolve(value), reason => reject(reason)) // 簡寫 result.then(resolve, reject) } else { resolve(result) } } module.exports = MyPromise
這裡then方法中的setTimeout
的作用並不是延遲執行,而是為了呼叫resolvePromise
函數時,保證建立的promise
存在。
到目前為止我們現實的Promise並沒有對異常做任何處理,為了保證程式碼的健壯性,我們需要對異常做一些處理。
現在我們需要對執行器進行異常捕獲,如果發生異常,就將我們的狀態修改為rejected
。
捕獲執行器的錯誤也比較簡單,只需要在建構函式中加入try...catch
語句就可以,
實現程式碼如下:
constructor(executor) { try { // 在呼叫executor需要傳遞兩個回撥函數,分別是resolve和reject executor(this.resolve, this.reject) } catch (e) { // 發生異常呼叫reject this.reject(e) } }
測試程式碼如下:
const MyPromise = require('./myPromise') let promise = new MyPromise((resolve, reject) => { throw new Error('執行器錯誤') }) promise.then(value => { console.log(value); }, error => { console.log(error.message); }) /* 輸出 執行器錯誤 */
現在我們需要對then中的異常捕獲到,並在下一次鏈式呼叫中傳遞到then的第二個函數中,實現的方式也是通過try...catch
語句,
範例程式碼如下:
// then方法的實現 then (onFulfilled, onRejected) { // then 方法返回一個MyPromise範例 const promise = new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 這裡並不需要延遲執行,而是通過setTimeout將其變成非同步函數 // 如果不變成非同步的話是在函數內獲取不到promise的 setTimeout(() => { try { // 將成功的值作為引數返回 // 儲存執行回撥函數的結果 const result = onFulfilled(this.value) // 如果返回的是一個普通的值,直接呼叫resolve // 如果是一個MyPromise範例,根據返回的解決來決定是呼叫resolve,還是reject resolvePromise(promise, result, resolve, reject) } catch (error) { reject(error) } }, 0) } else if (this.status === REJECTED) { // 將失敗的原因作為引數返回 onRejected(this.reason) } else { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 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的執行過程中遇到異常') }).then(null, reason => { console.log(reason.message) }) /* 輸出 resolve 成功 then的執行過程中遇到異常 */
現在只對成功狀態的then進行的鏈式呼叫以及錯誤處理,錯誤與非同步狀態未進行處理,其實處理起來也是一樣的,
範例程式碼如下:
// then方法的實現 then (onFulfilled, onRejected) { // then 方法返回一個MyPromise範例 const promise = new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 這裡並不需要延遲執行,而是通過setTimeout將其變成非同步函數 // 如果不變成非同步的話是在函數內獲取不到promise的 setTimeout(() => { try { // 將成功的值作為引數返回 // 儲存執行回撥函數的結果 const result = onFulfilled(this.value) // 如果返回的是一個普通的值,直接呼叫resolve // 如果是一個MyPromise範例,根據返回的解決來決定是呼叫resolve,還是reject resolvePromise(promise, result, resolve, reject) } catch (error) { reject(error) } }, 0) } else if (this.status === REJECTED) { // 失敗的處理同成功處理,只是呼叫的回撥函數不同 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的執行過程中遇到異常') }).then(null, reason => { console.log(reason.message) }) /* 輸出 resolve 成功 then的執行過程中遇到異常 */
Promise中的then方法其實是兩個可以可選引數,如果我們不傳遞任何引數的話,裡面的結果是向下傳遞的,直到捕獲為止,
例如下面這段程式碼:
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))
所以說我們只需要在沒有傳遞迴撥函數時,賦值一個預設的回撥函數即可。
實現程式碼如下:
// then方法的實現 then (onFulfilled, onRejected) { // 如果傳遞函數,就是用傳遞的函數,否則指定一個預設值,用於引數傳遞 onFulfilled = onFulfilled ? onFulfilled : value => value // 同理 onRejected = onRejected ? onRejected : reason => { throw reason } // then 方法返回一個MyPromise範例 const promise = new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) {... } else if (this.status === REJECTED) {... } else {... } }) return promise }
關於all()
方法的使用,可以引數Promise.all()
。簡單的說Promise.all()
會將多個Promise範例包裝為一個Promise範例,且順序與呼叫順序一致,
範例程式碼如下:
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
的執行是延遲了2s的,這裡如果不使用Promise.all()
的話最終順序是與我們呼叫不同的。
現在我們來分析一下all()的實現思路:
all()
方法可以通過類直接呼叫,所以是一個靜態方法all()
方法接收一個陣列,陣列中的值可以是一個普通值,也可以是一個MyPromise的範例物件rejecte
,如果是成功話將這個值存入一個陣列resolve
並將最終的結果陣列作為引數返回。實現程式碼:
/** * @description: 將多個Promise範例合併為一個Promise範例 * @param {*} array Promise或者普通值 * @returns {Promise} */ static all (array) { // 用於存放最終結果的陣列 let result = [] // 用於計算當前已經執行完的範例的數量 let count = 0 // 最後返回的是一個Promise範例 return new MyPromise((resolve, reject) => { /** * @description: 將執行完畢的值加入結果陣列,並根據實際情況決定是否呼叫resolve * @param {*} result 存放結果的陣列 * @param {*} index 要加入的索引 * @param {*} value 要加入陣列的值 * @param {*} resolve Promise中的resolve */ function addResult (result, index, value, resolve) { // 根據索引值,將結果堆入陣列中 result[index] = value // 執行完畢一個 count+1,如果當前值等於總長度的話說明已經執行結束了,可以直接呼叫resolve,說明已經成功執行完畢了 if (++count === array.length) { // 將執行結果返回 resolve(result) } } // 遍歷穿入的陣列,每個都執行then方法,獲取到最終的結果 array.forEach((p, index) => { // 判斷p是不是MyPromise的範例,如果是的話呼叫then方法,不是直接將值加入陣列中 if (p instanceof MyPromise) { p.then( // 成功時將結果直接加入陣列中 value => { addResult(result, index, value, resolve) }, // 如果失敗直接返回失敗原因 reason => { reject(reason) } ) } else { addResult(result, index, p, resolve) } }) }) }
關於Promise.resolve()
方法的用法可以參考Promise.resolve()
與Promise.reject()
。
我們實現的思路主要如下:
實現程式碼如下:
static resolve (value) { // 如果是MyPromise的範例,就直接返回這個範例 if (value instanceof MyPromise) return value // 如果不是的話建立一個MyPromise範例,並返回傳遞的值 return new MyPromise((resolve) => { resolve(value) }) }
關於finally方法可參考finally()
,實現該方法的實現程式碼如下:
finally (callback) { // 如何拿到當前的promise的狀態,使用then方法,而且不管怎樣都返回callback // 而且then方法就是返回一個promise物件,那麼我們直接返回then方法呼叫之後的結果即可 // 我們需要在回撥之後拿到成功的回撥,所以需要把value也return // 失敗的回撥也丟擲原因 // 如果callback是一個非同步的promise物件,我們還需要等待其執行完畢,所以需要用到靜態方法resolve return this.then(value => { // 把callback呼叫之後返回的promise傳遞過去,並且執行promise,且在成功之後返回value return MyPromise.resolve(callback()).then(() => value) }, reason => { // 失敗之後呼叫的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 // 兩秒之後執行p2 reject
關於catch方法可以參考catch()
,實現該方法其實非常簡單,只需要在內部呼叫then方法,不傳遞第一個回撥函數即可,
實現程式碼如下:
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 // 定義所有狀態的常數 const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' // Promise實質上就是一個類,首先建立一個Promise的類 class MyPromise { // 範例化Promise時需要一個回撥函數,該回撥函數立即執行 constructor(executor) { try { // 在呼叫executor需要傳遞兩個回撥函數,分別是resolve和reject executor(this.resolve, this.reject) } catch (e) { // 發生異常呼叫reject this.reject(e) } } // Promise 的狀態 status = PENDING // 記錄成功與失敗的值 value = undefined reason = undefined // 儲存成功與失敗的處理邏輯 onFulfilled = [] onRejected = [] resolve = (value) => {// 這裡使用箭頭函數是為了使其內部的this指向為該範例化後的物件 // 形參value表示,呼叫resolve時傳遞的引數 // 如果當前狀態不是pending,就直接跳出該邏輯 if (this.status !== PENDING) return // 將狀態修改為成功 this.status = FULFILLED // 將傳入的值進行儲存 this.value = value // 如果將狀態修復為成功,呼叫成功的回撥 // this.onFulfilled && this.onFulfilled(this.value) while (this.onFulfilled.length) { // Array.prototype.shift() 用於刪除陣列第一個元素,並返回 this.onFulfilled.shift()(this.value) } } reject = (reason) => {// 這裡使用箭頭函數是為了使其內部的this指向為該範例化後的物件 // 形參reason表示,呼叫reject時傳遞的失敗的原因 // 如果當前狀態不是pending,就直接跳出該邏輯 if (this.status !== PENDING) return // 將狀態修改為失敗 this.status = REJECTED // 儲存失敗的原因 this.reason = reason // 如果將狀態修復為失敗,呼叫失敗的回撥 // this.onRejected && this.onRejected(this.reason) while (this.onRejected.length) { // Array.prototype.shift() 用於刪除陣列第一個元素,並返回 this.onRejected.shift()(this.reason) } } // then方法的實現 then (onFulfilled, onRejected) { // 如果傳遞函數,就是用傳遞的函數,否則指定一個預設值,用於引數傳遞 onFulfilled = onFulfilled ? onFulfilled : value => value // 同理 onRejected = onRejected ? onRejected : reason => { throw reason } // then 方法返回一個MyPromise範例 const promise = new MyPromise((resolve, reject) => { // 判斷當前狀態,根據狀態呼叫指定回撥 if (this.status === FULFILLED) { // 這裡並不需要延遲執行,而是通過setTimeout將其變成非同步函數 // 如果不變成非同步的話是在函數內獲取不到promise的 setTimeout(() => { try { // 將成功的值作為引數返回 // 儲存執行回撥函數的結果 const result = onFulfilled(this.value) // 如果返回的是一個普通的值,直接呼叫resolve // 如果是一個MyPromise範例,根據返回的解決來決定是呼叫resolve,還是reject resolvePromise(promise, result, resolve, reject) } catch (error) { reject(error) } }, 0) } else if (this.status === REJECTED) { // 失敗的處理同成功處理,只是呼叫的回撥函數不同 setTimeout(() => { try { const result = onRejected(this.reason) resolvePromise(promise, result, resolve, reject) } catch (error) { reject(error) } }, 0) } else { // 表示既不是成功,也不是失敗。這個時候儲存傳遞進來的兩個回撥 // 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) { // 如何拿到當前的promise的狀態,使用then方法,而且不管怎樣都返回callback // 而且then方法就是返回一個promise物件,那麼我們直接返回then方法呼叫之後的結果即可 // 我們需要在回撥之後拿到成功的回撥,所以需要把value也return // 失敗的回撥也丟擲原因 // 如果callback是一個非同步的promise物件,我們還需要等待其執行完畢,所以需要用到靜態方法resolve return this.then(value => { // 把callback呼叫之後返回的promise傳遞過去,並且執行promise,且在成功之後返回value return MyPromise.resolve(callback()).then(() => value) }, reason => { // 失敗之後呼叫的then方法,然後把失敗的原因返回出去。 return MyPromise.resolve(callback()).then(() => { throw reason }) }) } /** * @description: 將多個Promise範例合併為一個Promise範例 * @param {*} array Promise或者普通值 * @returns {Promise} */ static all (array) { // 用於存放最終結果的陣列 let result = [] // 用於計算當前已經執行完的範例的數量 let count = 0 // 最後返回的是一個Promise範例 return new MyPromise((resolve, reject) => { /** * @description: 將執行完畢的值加入結果陣列,並根據實際情況決定是否呼叫resolve * @param {*} result 存放結果的陣列 * @param {*} index 要加入的索引 * @param {*} value 要加入陣列的值 * @param {*} resolve Promise中的resolve */ function addResult (result, index, value, resolve) { // 根據索引值,將結果堆入陣列中 result[index] = value // 執行完畢一個 count+1,如果當前值等於總長度的話說明已經執行結束了,可以直接呼叫resolve,說明已經成功執行完畢了 if (++count === array.length) { // 將執行結果返回 resolve(result) } } // 遍歷穿入的陣列,每個都執行then方法,獲取到最終的結果 array.forEach((p, index) => { // 判斷p是不是MyPromise的範例,如果是的話呼叫then方法,不是直接將值加入陣列中 if (p instanceof MyPromise) { p.then( // 成功時將結果直接加入陣列中 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 // 如果不是的話建立一個MyPromise範例,並返回傳遞的值 return new MyPromise((resolve) => { resolve(value) }) } } function resolvePromise (promise, result, resolve, reject) { // 這裡修改一下該函數,如果return的Promise範例物件,也就是傳入的promise===result的話,說明在promise中return的是當前promise物件。 if (promise === result) { // 這裡呼叫reject,並丟擲一個Error // return 是必須的,阻止程式向下執行 return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // 判斷傳遞的result是不是MyPromise的範例物件 if (result instanceof MyPromise) { // 說明是MyPromise的範例物件 // 呼叫.then方法,然後在回撥函數中獲取到具體的值,然後呼叫具體的回撥 // result.then(value => resolve(value), reason => reject(reason)) // 簡寫 result.then(resolve, reject) } else { resolve(result) } } module.exports = MyPromise
到此這篇關於萬字詳解JavaScript手寫一個Promise的文章就介紹到這了,更多相關JS手寫Promise內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45