開發中Promise
是及其經常使用的語法,基本上對於異步的處理大都是經過Promise
來進行完成。Promise規範有不少,ES6最終採用的是Promise/A+ 規範
,因此如下代碼也基本是基於這個規範來進行編寫的。javascript
首先咱們先列舉Promise的全部實例方法跟靜態方法html
實例方法前端
new Promise((resolve, reject) => {...}).then(() => {console.log('rsolve成功回調')}, () => {console.log('reject失敗回調')})
new Promise((resolve, reject) => {...}).catch(() => {console.log('reject失敗方法')})
new Promise((resolve, reject) => {...}).finally(() => {console.log('成功失敗都進入')})
Promise
靜態方法java
Promise.resolve(value)
返回Promise
實例Promise.reject(value)
返回Promise
實例Promise.all(promises)
: 傳入數組格式的Promise
並返回新的Promise
實例,成功便按照順序把值返回出來,其中一個失敗則直接變成失敗Promise.race(promises)
: 傳入數組格式的Promise
並返回新的Promise
實例,成功與失敗取決第一個的完成方式Promise
狀態一旦肯定變不可再發生變化,有如下三個狀態:pending
、fulfilled
、rejected
Promise
在瀏覽器中的實現是放於微任務隊列中的,須要作微任務的處理(JavaScript中的Event Loop(事件循環)機制
)node
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw 'Promise resolver undefined is not a function' } /* new Promise((resolve, reject) => { resolve: 成功 reject: 失敗 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } // 接收1-2參數,第一個爲成功的回調,第二個爲失敗的回調 then(onFulfilled, onRejected) { // 有可能已經resolve了,由於Promise能夠提早resolve,而後then方法後面註冊 if (this._state === 'fulfilled') { onFulfilled?.(this._value) return } // reject同理 if (this._state === 'rejected') { onRejected?.(this._value) return } // Promise尚未完成,push到一個隊列,到時候完成的時候,執行這個隊列裏面對應的函數 this._queue.push({ onFulfilled, onRejected, }) } // 接收失敗的回調 catch(onRejected) { // 至關於直接調用then傳入失敗的回調 this.then(null, onRejected) } // 成功與失敗都執行的回調 finally(onDone) { const fn = () => onDone() this.then(fn, fn) } // 成功resolve _resolve(value) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return this._state = 'fulfilled' // 把值存起來,當再次調用的時候直接取這個值就行,由於Promise一旦肯定就不會發生改變了 this._value = value // 執行前面.then方法裏面push函數形式的參數,這樣就執行對應的方法了。 this._queue.forEach((callback) => { callback.onFulfilled?.(this._value) }) } // 失敗reject _reject(error) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return this._state = 'rejected' this._value = error this._queue.forEach((callback) => { callback.onRejected?.(this._value) }) } }
調用邏輯:git
then
方法傳入函數形式的參數,也就是onFulfilled
=> then((onFulfilled, onRejected) => {...})
then
方法中把onFulfilled
函數放入_queue
這個集合中。 => this._queue.push({ onFulfilled, onRejected })
resolve
函數,這個時候就調用_queue
收集好的經過then
方法註冊的函數。統一執行這些函數,這樣就達到異步回調完成,執行對應的then
方法裏面的函數// 結果打印 const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) p.then((res) => { console.log(res) // => success }) // reject const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject('fail') }, 1000) }) p1.catch((res) => { console.log(res) // => fail }) // finally const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 1000) }) p2.finally(() => { console.log('done') // => done })
在線代碼演示
在瀏覽器中 Promise
完成以後會被推入微任務,因此咱們也須要進行這塊的處理。瀏覽器中使用MutationObserver,node可使用process.nextTick
github
class Promise { ... // 推入微任務 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 瀏覽器經過MutationObserver實現微任務的效果 // 這塊能夠單獨拿出來共用,避免沒必要要的開銷,否則每次都須要生成節點。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node端經過process.nextTick來實現 process.nextTick(fn) } else { setTimeout(fn, 0) } } // 成功resolve _resolve(value) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return // 推入微任務 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((callback) => { callback.onFulfilled?.(this._value) }) }) } // 失敗reject _reject(error) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return // 推入微任務 this._nextTick(() => { this._state = 'rejected' this._value = error this._queue.forEach((callback) => { callback.onRejected?.(this._value) }) }) } ... }
效果演示
一般Promise
會處理多個異步請求,有時候請求之間是有相互依賴關係的。segmentfault
例如:數組
const getUser = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ userId: '123' }) }, 500) }) } const getDataByUser = (userId) => { return new Promise((resolve, reject) => { setTimeout(() => { // .... resolve({a: 1}) }, 500) }) } // 使用 getUser().then((user) => { return getDataByUser(user.userId) }).then((res) => { console.log(res)// {a: 1} })
getDataByUser
依賴getUser
請求回來的用戶信息,這裏就須要用到Promise
鏈式的調用,下面咱們來改動咱們的代碼promise
class Promise { constructor(fn) { fn(this._resolve.bind(this), this._reject.bind(this)) } ... // 1. 這時候then方法須要返回新的Promise了,由於須要進行鏈式調用,而且下一個then方法接受上一個then方法的值 // 2. 返回的Promise確定是一個新的Promise,否則就會共用狀態跟返回結果了。 // 3. 把上一個then方法中的返回值當作下一個Promise resolve的值 then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能已經resolve了,由於Promise能夠提早resolve,而後then方法後面註冊,這個時候能夠直接把值返給函數就行了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } /* 把當前Promise的then方法的參數跟新的Promise的resolve, reject存到一塊兒,以此來作關聯。 這樣就能把上一個Promise中的onFulfilled與新的Promise中的resolve兩個關聯到一塊,而後即可以作賦值之類的操做了。reject同理 */ this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } // reject同理 _resolve(value) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return // 上面示例裏面其實返回的是一個Promise,而不是直接返回的值,因此,這裏咱們須要作一個特殊處理。 // 就是resolve()的值若是是Promise的對象,咱們須要解析Promise的結果,而後在把值傳給resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱們能夠把當前_resolve方法傳遞下去,由於then方法中的參數,一經下個Promise resolve,便會執行then方法對應的參數,而後把對應的值傳入。 // 這樣就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 推入微任務 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((obj) => { // 接受onFulfilled返回值 const val = obj.onFulfilled?.(this._value) // reoslve這個值,此時 onFulfilled 是當前Promise then方法中的第一個參數: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函數,這樣就把then方法中的返回值傳給下一個Promise obj.resolve(val) }) }) } ... }
效果演示
調用邏輯:
MutationObserver
跟process.nextTick
來進行實現Promise
鏈式調用,這裏經過把then
方法中的(onFulfilled, onRejected)
參數與新返回的Promise
中的(resolve, reject)
關聯到一塊兒。Promise
成功,調用onFulfilled
函數,就能夠把onFulfilled
中返回的值,放到新的Promise的resolve中。resolve
的值是Promise
對象,遞歸進行解析,而後再把值返回出去完整代碼
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw new Error('Promise resolver undefined is not a function') } /* new Promise((resolve, reject) => { resolve: 成功 reject: 失敗 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } // 接收1-2參數,第一個爲成功的回調,第二個爲失敗的回調 then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能已經resolve了,由於Promise能夠提早resolve,而後then方法後面註冊,這個時候能夠直接把值返給函數就行了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } // 把當前Promise的then方法的參數跟新的Promise的resolve, reject存到一塊兒,以此來作關聯 this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } // 接收失敗的回調 catch(onRejected) { return this.then(null, onRejected) } // 成功與失敗都執行的回調 finally(onDone) { return this.then((value) => { onDone() return value }, (value) => { // console.log(value) onDone() throw value }) } // 推入微任務 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 瀏覽器 // 這塊能夠單獨拿出來共用,避免沒必要要的開銷,否則每次都須要生成節點。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node process.nextTick(fn) } else { setTimeout(fn, 0) } } // 成功resolve _resolve(value) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return // 上面示例裏面其實返回的是一個Promise,而不是直接返回的值,因此,這裏咱們須要作一個特殊處理。 // 就是若是resolve()的若是是Promise的對象,咱們須要解析Promise的結果,而後在把值傳給resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱們能夠把當前_resolve方法傳遞下去,由於then方法中的參數,一經下個Promise resolve,便會執行then方法對應的參數,而後把對應的值傳入。 // 這樣就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 推入微任務 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((obj) => { // 使用try catch 來捕獲onFulfilled存在函數內部錯誤的狀況 try { // 接受onFulfilled返回值,若是不存在,把this._value往下傳遞 const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value // reoslve這個值,此時 onFulfilled 是當前Promise then方法中的第一個參數: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函數,這樣就把then方法中的返回值傳給下一個Promise obj.resolve(val) } catch (e) { obj.reject(e) } }) }) } // 失敗reject _reject(error) { if (this._state !== 'pending') return this._nextTick(() => { this._state = 'rejected' this._value = error this._queue.forEach((obj) => { try { const val = obj.onRejected ? obj.onRejected(this._value) : this._value // 當前 reject執行完畢以後,會返回新的Promise,應該是能正常resolve的,因此這裏要用 resolve, 不該該繼續使用reject來讓下個Promise執行失敗流程 obj.resolve(val) } catch (e) { obj.reject(e) } }) }) } }
總共有4個靜態方法: Promise.resolve
、Promise.reject
、Promise.all
、Promise.race
,統一返回的都是新的Promise。
class Promise { ... /** * 直接resolve */ static resolve(value) { // 是Promise直接返回 if (value instanceof Promise) { return value } else if (typeof value === 'object' && typeof value.then === 'function') { // 傳入的對象含有then方法 const then = value.then return new Promise((resolve) => { then.call(value, resolve) }) } else { // 正常返回值,直接返回新的Promise在resolve這個值 return new Promise((resolve) => resolve(value)) } } /** * 直接reject, 測試下Promise.reject並沒作特殊處理,因此直接返回便可。 */ static reject(value) { return new Promise((resolve, reject) => reject(value)) } /** * 傳入數組格式的`Promise`並返回新的`Promise`實例,成功便按照順序把值返回出來,其中一個失敗則直接變成失敗 */ static all(promises) { return new Promise((resolve, reject) => { let count = 0 let arr = [] // 按照對應的下標push到數組裏面 promises.forEach((promise, index) => { // 轉換成Promise對象 Promise.resolve(promise).then((res) => { count++ arr[index] = res if (count === promises.length) { resolve(arr) } }, err => reject(err)) }) }) } /** * 傳入數組格式的`Promise`並返回新的`Promise`實例,成功與失敗取決第一個的完成方式 */ static race(promises) { return new Promise((resolve, reject) => { promises.forEach((promise, index) => { // 轉換成Promise對象 Promise.resolve(promise).then((res) => { // 誰先執行直接resolve, 或reject resolve(res) }, err => reject(err)) }) }) } ... }
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw new Error('Promise resolver undefined is not a function') } /* new Promise((resolve, reject) => { resolve: 成功 reject: 失敗 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } /** * 接收1-2參數,第一個爲成功的回調,第二個爲失敗的回調 * * @param {*} onFulfilled * @param {*} onRejected * @return {*} * @memberof Promise */ then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能已經resolve了,由於Promise能夠提早resolve,而後then方法後面註冊,這個時候能夠直接把值返給函數就行了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } // 把當前Promise的then方法的參數跟新的Promise的resolve, reject存到一塊兒,以此來作關聯 this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } /** * 接收失敗的回調 * * @param {*} onRejected * @return {*} * @memberof Promise */ catch(onRejected) { return this.then(null, onRejected) } /** * 成功與失敗都執行的回調 * * @param {*} onDone * @return {*} * @memberof Promise */ finally(onDone) { return this.then((value) => { onDone() return value }, (value) => { onDone() // 直接報錯,能夠在try catch中捕獲錯誤 throw value }) } /** * 直接resolve * * @static * @param {*} value * @return {*} * @memberof Promise */ static resolve(value) { if (value instanceof Promise) { return value } else if (typeof value === 'object' && typeof value.then === 'function') { // 傳入的對象含有then方法 const then = value.then return new Promise((resolve) => { then.call(value, resolve) }) } else { return new Promise((resolve) => resolve(value)) } } /** * 直接reject, 測試下reject在Promise.reject中沒作特殊處理 * * @static * @param {*} value * @return {*} * @memberof Promise */ static reject(value) { return new Promise((resolve, reject) => reject(value)) } /** * 傳入數組格式的`Promise`並返回新的`Promise`實例,成功便按照順序把值返回出來,其中一個失敗則直接變成失敗 * * @static * @param {*} promises * @memberof Promise */ static all(promises) { return new Promise((resolve, reject) => { let count = 0 let arr = [] if (Array.isArray(promises)) { if (promises.length === 0) { return resolve(promises) } promises.forEach((promise, index) => { // 轉換成Promise對象 Promise.resolve(promise).then((res) => { count++ arr[index] = res if (count === promises.length) { resolve(arr) } }, err => reject(err)) }) return } else { reject(`${promises} is not Array`) } }) } /** * 傳入數組格式的`Promise`並返回新的`Promise`實例,成功與失敗取決第一個的完成方式 * * @static * @param {*} promises * @return {*} * @memberof Promise */ static race(promises) { return new Promise((resolve, reject) => { if (Array.isArray(promises)) { promises.forEach((promise, index) => { // 轉換成Promise對象 Promise.resolve(promise).then((res) => { resolve(res) }, err => reject(err)) }) } else { reject(`${promises} is not Array`) } }) } // 推入微任務 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 瀏覽器 // 這塊能夠單獨拿出來共用,避免沒必要要的開銷,否則每次都須要生成節點。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node process.nextTick(fn) } else { setTimeout(fn, 0) } } // 成功resolve _resolve(value) { // 狀態肯定了,就再也不發生變化了 if (this._state !== 'pending') return // 上面示例裏面其實返回的是一個Promise,而不是直接返回的值,因此,這裏咱們須要作一個特殊處理。 // 就是若是resolve()的若是是Promise的對象,咱們須要解析Promise的結果,而後在把值傳給resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱們能夠把當前_resolve方法傳遞下去,由於then方法中的參數,一經下個Promise resolve,便會執行then方法對應的參數,而後把對應的值傳入。 // 這樣就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 經過打印測試,若是直接在線程裏進行resolve, 狀態跟值好像是直接就改變了,並無執行完主流程,在執行微任務的時候進行修改的。 // 因此把狀態改變和值的修改移出了微任務,只有在走回調的時候才經過微任務進行處理 this._state = 'fulfilled' this._value = value // 推入微任務 this._nextTick(() => { this._queue.forEach((obj) => { // 使用try catch 來捕獲onFulfilled存在函數內部錯誤的狀況 try { // 接受onFulfilled返回值,若是不存在,把this._value往下傳遞 const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value // reoslve這個值,此時 onFulfilled 是當前Promise then方法中的第一個參數: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函數,這樣就把then方法中的返回值傳給下一個Promise obj.resolve(val) } catch (e) { obj.reject(e) } }) }) } // 失敗reject _reject(error) { if (this._state !== 'pending') return this._state = 'rejected' this._value = error this._nextTick(() => { this._queue.forEach((obj) => { try { // 用戶傳入的函數內部錯誤捕獲 if (obj.onRejected) { const val = obj.onRejected(this._value) // 當前 reject執行完畢以後,會返回新的Promise,應該是能正常resolve的,因此這裏要用 resolve, 不該該繼續使用reject來讓下個Promise執行失敗流程 obj.resolve(val) } else { // 遞歸傳遞reject錯誤 obj.reject(this._value) } } catch (e) { obj.reject(e) } }) }) } }
本項目完整代碼:GitHub
以上就是Promise的實現方案,固然這個跟完整的Promises/A+規範
是有區別的。這裏只是用作於學習之用。
QQ羣 | 公衆號 |
---|---|
前端打雜羣![]() |
冬瓜書屋![]() |
我本身新建立了一個相互學習的羣,不管你是準備入坑的小白,仍是半路入行的同窗,但願咱們能一塊兒分享與交流。
QQ羣:810018802, 點擊加入