本篇文章主要在於探究Promise
的實現原理,帶領你們一步一步實現一個Promise
, 不對其用法作說明,若是讀者還對Promise的用法不瞭解,能夠查看阮一峯老師的 ES6 Promise教程。
接下來,帶你一步一步實現一個 Promise
javascript
Promise
基本結構new Promise((resolve, reject) => { setTimeout(() => { resolve('FULFILLED') }, 1000) })
構造函數Promise
必須接受一個函數做爲參數,咱們稱該函數爲handle
,handle
又包含resolve
和reject
兩個參數,它們是兩個函數。
定義一個判斷一個變量是否爲函數的方法,後面會用到java
// 判斷變量否爲function const isFunction = variable => typeof variable === 'function'
首先,咱們定義一個名爲 MyPromise
的 Class
,它接受一個函數 handle
做爲參數es6
class MyPromise { constructor (handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } } }
再往下看數組
Promise
狀態和值Promise
對象存在如下三種狀態:promise
Pending(進行中)
Fulfilled(已成功)
Rejected(已失敗)
狀態只能由Pending
變爲Fulfilled
或由Pending
變爲Rejected
,且狀態改變以後不會在發生變化,會一直保持這個狀態。
Promise
的值是指狀態改變時傳遞給回調函數的值異步
上文中handle
函數包含resolve
和reject
兩個參數,它們是兩個函數,能夠用於改變Promise
的狀態和傳入Promise
的值
new Promise((resolve, reject) => { setTimeout(() => { resolve('FULFILLED') }, 1000) })
這裏 resolve
傳入的 "FULFILLED"
就是 Promise
的值函數
resolve
和 reject
this
resolve
: 將Promise對象的狀態從 Pending(進行中)
變爲 Fulfilled(已成功)
reject
: 將Promise對象的狀態從 Pending(進行中)
變爲 Rejected(已失敗)
resolve
和 reject
均可以傳入任意類型的值做爲實參,表示 Promise
對象成功(Fulfilled)
和失敗(Rejected)
的值瞭解了 Promise
的狀態和值,接下來,咱們爲 MyPromise
添加狀態屬性和值code
首先定義三個常量,用於標記Promise對象的三種狀態
// 定義Promise的三種狀態常量 const PENDING = 'PENDING' const FULFILLED = 'FULFILLED' const REJECTED = 'REJECTED'
再爲
MyPromise
添加狀態和值,並添加狀態改變的執行邏輯
class MyPromise { constructor (handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } // 添加狀態 this._status = PENDING // 添加狀態 this._value = undefined // 執行handle try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } // 添加resovle時執行的函數 _resolve (val) { if (this._status !== PENDING) return this._status = FULFILLED this._value = val } // 添加reject時執行的函數 _reject (err) { if (this._status !== PENDING) return this._status = REJECTED this._value = err } }
這樣就實現了 Promise
狀態和值的改變。下面說一說 Promise
的核心: then
方法對象
Promise
的 then
方法Promise
對象的 then
方法接受兩個參數:
promise.then(onFulfilled, onRejected)
參數可選
onFulfilled
和 onRejected
都是可選參數。
onFulfilled
或 onRejected
不是函數,其必須被忽略onFulfilled
特性
若是 onFulfilled
是函數:
promise
狀態變爲成功時必須被調用,其第一個參數爲 promise
成功狀態傳入的值( resolve
執行時傳入的值)promise
狀態改變前其不可被調用onRejected
特性
若是 onRejected
是函數:
promise
狀態變爲失敗時必須被調用,其第一個參數爲 promise
失敗狀態傳入的值( reject
執行時傳入的值)promise
狀態改變前其不可被調用屢次調用
then
方法能夠被同一個 promise
對象調用屢次
promise
成功狀態時,全部 onFulfilled
需按照其註冊順序依次回調promise
失敗狀態時,全部 onRejected
需按照其註冊順序依次回調返回
then
方法必須返回一個新的 promise
對象
promise2 = promise1.then(onFulfilled, onRejected);
所以 promise
支持鏈式調用
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
這裏涉及到 Promise
的執行規則,包括「值的傳遞」和「錯誤捕獲」機制:
一、若是 onFulfilled
或者 onRejected
返回一個值 x
,則運行下面的 Promise
解決過程:[[Resolve]](promise2, x)
x
不爲 Promise
,則使 x
直接做爲新返回的 Promise
對象的值, 即新的onFulfilled
或者 onRejected
函數的參數.x
爲 Promise
,這時後一個回調函數,就會等待該 Promise
對象(即 x
)的狀態發生變化,纔會被調用,而且新的 Promise
狀態和 x
的狀態相同。下面的例子用於幫助理解:
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 1000) }) promise2 = promise1.then(res => { // 返回一個普通值 return '這裏返回一個普通值' }) promise2.then(res => { console.log(res) //1秒後打印出:這裏返回一個普通值 })
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 1000) }) promise2 = promise1.then(res => { // 返回一個Promise對象 return new Promise((resolve, reject) => { setTimeout(() => { resolve('這裏返回一個Promise') }, 2000) }) }) promise2.then(res => { console.log(res) //3秒後打印出:這裏返回一個Promise })
二、若是 onFulfilled
或者 onRejected
拋出一個異常 e
,則 promise2
必須變爲失敗(Rejected)
,並返回失敗的值 e
,例如:
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) promise2 = promise1.then(res => { throw new Error('這裏拋出一個異常e') }) promise2.then(res => { console.log(res) }, err => { console.log(err) //1秒後打印出:這裏拋出一個異常e })
三、若是 onFulfilled
不是函數且 promise1
狀態爲成功(Fulfilled)
, promise2
必須變爲成功(Fulfilled)
並返回 promise1
成功的值,例如:
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) promise2 = promise1.then('這裏的onFulfilled原本是一個函數,但如今不是') promise2.then(res => { console.log(res) // 1秒後打印出:success }, err => { console.log(err) })
四、若是 onRejected
不是函數且 promise1
狀態爲失敗(Rejected)
,promise2
必須變爲失敗(Rejected)
並返回 promise1
失敗的值,例如:
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject('fail') }, 1000) }) promise2 = promise1.then(res => res, '這裏的onRejected原本是一個函數,但如今不是') promise2.then(res => { console.log(res) }, err => { console.log(err) // 1秒後打印出:fail })
根據上面的規則,咱們來爲 完善 MyPromise
修改 constructor
: 增長執行隊列
因爲 then
方法支持屢次調用,咱們能夠維護兩個數組,將每次 then
方法註冊時的回調函數添加到數組中,等待執行
constructor (handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } // 添加狀態 this._status = PENDING // 添加狀態 this._value = undefined // 添加成功回調函數隊列 this._fulfilledQueues = [] // 添加失敗回調函數隊列 this._rejectedQueues = [] // 執行handle try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } }
添加then方法
首先,then
返回一個新的 Promise
對象,而且須要將回調函數加入到執行隊列中
// 添加then方法 then (onFulfilled, onRejected) { const { _value, _status } = this switch (_status) { // 當狀態爲pending時,將then方法回調函數加入執行隊列等待執行 case PENDING: this._fulfilledQueues.push(onFulfilled) this._rejectedQueues.push(onRejected) break // 當狀態已經改變時,當即執行對應的回調函數 case FULFILLED: onFulfilled(_value) break case REJECTED: onRejected(_value) break } // 返回一個新的Promise對象 return new MyPromise((onFulfilledNext, onRejectedNext) => { }) }
那返回的新的 Promise
對象何時改變狀態?改變爲哪一種狀態呢?
根據上文中 then
方法的規則,咱們知道返回的新的 Promise
對象的狀態依賴於當前 then
方法回調函數執行的狀況以及返回值,例如 then
的參數是否爲一個函數、回調函數執行是否出錯、返回值是否爲 Promise
對象。
咱們來進一步完善 then
方法:
// 添加then方法 then (onFulfilled, onRejected) { const { _value, _status } = this // 返回一個新的Promise對象 return new MyPromise((onFulfilledNext, onRejectedNext) => { // 封裝一個成功時執行的函數 let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { // 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調 res.then(onFulfilledNext, onRejectedNext) } else { //不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數 onFulfilledNext(res) } } } catch (err) { // 若是函數執行出錯,新的Promise對象的狀態爲失敗 onRejectedNext(err) } } // 封裝一個失敗時執行的函數 let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { // 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調 res.then(onFulfilledNext, onRejectedNext) } else { //不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數 onFulfilledNext(res) } } } catch (err) { // 若是函數執行出錯,新的Promise對象的狀態爲失敗 onRejectedNext(err) } } switch (_status) { // 當狀態爲pending時,將then方法回調函數加入執行隊列等待執行 case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break // 當狀態已經改變時,當即執行對應的回調函數 case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) }
這一部分可能不太好理解,讀者須要結合上文中
then
方法的規則來細細的分析。
接着修改 _resolve
和 _reject
:依次執行隊列中的函數
當 resolve
或 reject
方法執行時,咱們依次提取成功或失敗任務隊列當中的函數開始執行,並清空隊列,從而實現 then
方法的屢次調用,實現的代碼以下:
// 添加resovle時執行的函數 _resolve (val) { if (this._status !== PENDING) return // 依次執行成功隊列中的函數,並清空隊列 const run = () => { this._status = FULFILLED this._value = val let cb; while (cb = this._fulfilledQueues.shift()) { cb(val) } } // 爲了支持同步的Promise,這裏採用異步調用 setTimeout(() => run(), 0) } // 添加reject時執行的函數 _reject (err) { if (this._status !== PENDING) return // 依次執行失敗隊列中的函數,並清空隊列 const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } // 爲了支持同步的Promise,這裏採用異步調用 setTimeout(run, 0) }
這裏還有一種特殊的狀況,就是當 resolve
方法傳入的參數爲一個 Promise
對象時,則該 Promise
對象狀態決定當前 Promise
對象的狀態。
const p1 = new Promise(function (resolve, reject) { // ... }); const p2 = new Promise(function (resolve, reject) { // ... resolve(p1); })
上面代碼中,p1
和 p2
都是 Promise
的實例,可是 p2
的resolve
方法將 p1
做爲參數,即一個異步操做的結果是返回另外一個異步操做。
注意,這時 p1
的狀態就會傳遞給 p2
,也就是說,p1
的狀態決定了 p2
的狀態。若是 p1
的狀態是Pending
,那麼 p2
的回調函數就會等待 p1
的狀態改變;若是 p1
的狀態已是 Fulfilled
或者 Rejected
,那麼 p2
的回調函數將會馬上執行。
咱們來修改_resolve
來支持這樣的特性
// 添加resovle時執行的函數 _resolve (val) { const run = () => { if (this._status !== PENDING) return // 依次執行成功隊列中的函數,並清空隊列 const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } // 依次執行失敗隊列中的函數,並清空隊列 const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 若是resolve的參數爲Promise對象,則必須等待該Promise對象狀態改變後, 當前Promsie的狀態纔會改變,且狀態取決於參數Promsie對象的狀態 */ if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } // 爲了支持同步的Promise,這裏採用異步調用 setTimeout(run, 0) }
這樣一個Promise就基本實現了,如今咱們來加一些其它的方法
catch
方法
至關於調用then
方法, 但只傳入Rejected
狀態的回調函數
// 添加catch方法 catch (onRejected) { return this.then(undefined, onRejected) }
靜態 resolve
方法
// 添加靜態resolve方法 static resolve (value) { // 若是參數是MyPromise實例,直接返回這個實例 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) }
靜態 reject
方法
// 添加靜態reject方法 static reject (value) { return new MyPromise((resolve ,reject) => reject(value)) }
靜態 all
方法
// 添加靜態all方法 static all (list) { return new MyPromise((resolve, reject) => { /** * 返回值的集合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 數組參數若是不是MyPromise實例,先調用MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 全部狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled if (count === list.length) resolve(values) }, err => { // 有一個被rejected時返回的MyPromise狀態就變成rejected reject(err) }) } }) }
靜態 race
方法
// 添加靜態race方法 static race (list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只要有一個實例率先改變狀態,新的MyPromise的狀態就跟着改變 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) }
finally
方法
finally
方法用於指定無論Promise
對象最後狀態如何,都會執行的操做
finally (cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); };
這樣一個完整的 Promsie
就實現了,你們對 Promise
的原理也有了解,可讓咱們在使用Promise的時候更加清晰明瞭。
完整代碼以下
// 判斷變量否爲function const isFunction = variable => typeof variable === 'function' // 定義Promise的三種狀態常量 const PENDING = 'PENDING' const FULFILLED = 'FULFILLED' const REJECTED = 'REJECTED' class MyPromise { constructor (handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } // 添加狀態 this._status = PENDING // 添加狀態 this._value = undefined // 添加成功回調函數隊列 this._fulfilledQueues = [] // 添加失敗回調函數隊列 this._rejectedQueues = [] // 執行handle try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } // 添加resovle時執行的函數 _resolve (val) { const run = () => { if (this._status !== PENDING) return // 依次執行成功隊列中的函數,並清空隊列 const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } // 依次執行失敗隊列中的函數,並清空隊列 const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 若是resolve的參數爲Promise對象,則必須等待該Promise對象狀態改變後, 當前Promsie的狀態纔會改變,且狀態取決於參數Promsie對象的狀態 */ if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } // 爲了支持同步的Promise,這裏採用異步調用 setTimeout(run, 0) } // 添加reject時執行的函數 _reject (err) { if (this._status !== PENDING) return // 依次執行失敗隊列中的函數,並清空隊列 const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } // 爲了支持同步的Promise,這裏採用異步調用 setTimeout(run, 0) } // 添加then方法 then (onFulfilled, onRejected) { const { _value, _status } = this // 返回一個新的Promise對象 return new MyPromise((onFulfilledNext, onRejectedNext) => { // 封裝一個成功時執行的函數 let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { // 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調 res.then(onFulfilledNext, onRejectedNext) } else { //不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數 onFulfilledNext(res) } } } catch (err) { // 若是函數執行出錯,新的Promise對象的狀態爲失敗 onRejectedNext(err) } } // 封裝一個失敗時執行的函數 let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { // 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調 res.then(onFulfilledNext, onRejectedNext) } else { //不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數 onFulfilledNext(res) } } } catch (err) { // 若是函數執行出錯,新的Promise對象的狀態爲失敗 onRejectedNext(err) } } switch (_status) { // 當狀態爲pending時,將then方法回調函數加入執行隊列等待執行 case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break // 當狀態已經改變時,當即執行對應的回調函數 case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } // 添加catch方法 catch (onRejected) { return this.then(undefined, onRejected) } // 添加靜態resolve方法 static resolve (value) { // 若是參數是MyPromise實例,直接返回這個實例 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } // 添加靜態reject方法 static reject (value) { return new MyPromise((resolve ,reject) => reject(value)) } // 添加靜態all方法 static all (list) { return new MyPromise((resolve, reject) => { /** * 返回值的集合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 數組參數若是不是MyPromise實例,先調用MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 全部狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled if (count === list.length) resolve(values) }, err => { // 有一個被rejected時返回的MyPromise狀態就變成rejected reject(err) }) } }) } // 添加靜態race方法 static race (list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只要有一個實例率先改變狀態,新的MyPromise的狀態就跟着改變 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } finally (cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); } }
若是以爲還行的話,點個贊、收藏一下再走吧。