在面試中,Promise會被經常問,下面為大家整合關于Promise各種問題。
Promise核心原理實現
用Promise來寫代碼
Promise的使用分析
Promise是一個在執行的時候,需要傳遞一個執行器(回調函數)進去,執行器會立即執行的一一個類。
Promise中的狀態分為三個,分別是:
pending→等待
fulfilled→成功
rejected→失敗
狀態的切換只有兩種,分別是:
pending→fulfilled
pending→rejected
須知:當狀態發生改變時就不會再次改變:
執行器中的兩個參數,分別是resolve和reject,這兩個回調函數,調用resolve是從pending狀態到fulfilled,調用reject是從狀態pending到rejected。傳遞給這兩個回調函數的參數會作為成功或失敗的值。
Promise的實例對象具有一個then方法,該方法接受兩個回調函數,分別來處理成功與失敗的狀態,then方法內部會進行判斷,然后根據當前狀態確定調用的回調函數。then方法應該是被定義在原型對象中的。
then的回調函數有一個值,當達到這個值時候,表示成功后返回的值;當返回不是這值時,就表示失敗。
MyPromise的實現
根據我們上面的分析,寫出如下代碼:
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中加入異步操作
當代碼中存在異步操作時,表示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后會成功輸出成功。
實現then方法的多次調用
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的鏈式調用
想要實現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
then方法鏈式調用識別Promise對象自返回
在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存在。
捕獲錯誤及 then 鏈式調用其他狀態代碼補充
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中的異常捕獲到,并在下一次鏈式調用中傳遞到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的執行過程中遇到異常 */
將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 }
Promise.all方法的實現
關于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的實例對象
return一個新的MyPromise實例對象
遍歷數組中的每一個值,判斷值得類型,如果是一個普通值得話直接將值存入一個數組;如果是一個MyPromise的實例對象的話,會調用then方法,然后根據執行后的狀態,如果失敗的話調用新的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.resolve()與Promise.reject()。
我們實現的思路主要如下:
該方法是一個靜態方法
該方法接受的如果是一個值就將該值包裝為一個MyPromise的實例對象返回,如果是一個MyPromise的實例對象,調用then方法返回。
實現代碼如下:
static resolve (value) { // 如果是MyPromise的實例,就直接返回這個實例 if (value instanceof MyPromise) return value // 如果不是的話創建一個MyPromise實例,并返回傳遞的值 return new MyPromise((resolve) => { resolve(value) }) }
finally方法的實現
關于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方法可以參考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
上面內容很多,大家可以慢慢消化。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/127765.html
摘要:本文從入手,系統的回顧的異步機制及發展歷程。需要提醒的是,文本沒有討論的異步機制。本文是專題系列文章之一,后續會有更多專題推出地址持續更新博客地址文章排版真的很漂亮如果覺得對你有幫助,歡迎來點或者來我的博客親口告訴我本文從Event Loop、Promise、Generator、async await入手,系統的回顧 JavaScript 的異步機制及發展歷程。 需要提醒的是,文本沒有討論 ...
摘要:本文從入手,系統的回顧的異步機制及發展歷程。需要提醒的是,文本沒有討論的異步機制。這就是之前提到的事件觸發線程。其實無論是請求還是定時器還是事件,我們都可以統稱它們為事件。第二階段,引擎線程專注于處理事件。將外元素的事件回調放入調用棧。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....
JavaScript筆試部分 點擊關注本公眾號獲取文檔最新更新,并可以領取配套于本指南的 《前端面試手冊》 以及最標準的簡歷模板. 實現防抖函數(debounce) 防抖函數原理:在事件被觸發n秒后再執行回調,如果在這n秒內又被觸發,則重新計時。 那么與節流函數的區別直接看這個動畫實現即可。 showImg(https://segmentfault.com/img/remote/146000002...
摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關問題,其他公司壓根沒問。我自己回答的是自己開發組件面臨的問題。完全不用擔心對方到時候打電話核對的問題。 2019的5月9號,離發工資還有1天的時候,我的領導親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經是第2個月沒工資了。 公...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28