注: 本文中寫的類只是爲了瞭解Promise
類的內部原理而模擬出來一個, 並不必定符合相似的規範或者效率多麼高, 可是基本的功能仍是實現了的.
注: 本文代碼運行環境: NodeJS v14.9.0數組
以下, 這是一個傳統的使用回調函數的異步代碼promise
function getAnInt(callback) { setTimeout(() => { callback(81) }, 500) } function sqrt(n, resolve, reject) { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500) } let errHandler = err => console.log("Error " + err) getAnInt(v1 => { console.log(v1) sqrt(v1, v2 => { console.log(v2) sqrt(v2, v3 => { console.log(v3) sqrt(v3, v4 => { console.log(v4) }, errHandler) }, errHandler) }, errHandler) })
執行結果:框架
81 9 3 Error cannot get an int
有沒有感受眼花繚亂? 這金字塔狀的代碼被親切地稱爲回調地獄, 下面就是咱們的主角Promise
上場的時候了, 醬醬醬醬異步
function getAnInt() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(81) }, 500) }) } function sqrt(n) { return new Promise((resolve, reject) => { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500) }) } getAnInt().then(v1 => { console.log(v1) return sqrt(v1) }).then(v2 => { console.log(v2) return sqrt(v2) }).then(v3 => { console.log(v3) return sqrt(v3) }).then(v4 => { console.log(v4) }).catch(err => { console.log("Error " + err) })
執行結果:函數
81 9 3 Error cannot get an int
結果如出一轍, 可是這個代碼寫出來的感受, 就是要清晰了好多好多好多好多好多好多好多好多好多好多好多好多測試
在Promise/A+標準中定義了Promise
究竟是個什麼東西, 這裏挑出重點部分, 其他的規範若是想看的話點這裏去官網this
promise
含有then
方法, 沒有規定其它的方法.then
方法會返回一個新的promise
then
方法的參數是onFulfilled, onRejected
, 它們都是可選的(固然都是函數類型)promise
有三個狀態,pending(代辦)
,fulfilled(完成)
和rejected(被拒絕)
, 狀態只能從pending
轉成另外兩個, 而後就不能再轉了.- 若是
onRejected
或者onFulfilled
返回了一個Promise
對象, 須要得出它的結果再傳給下一個then
方法裏對應的地方
由於本文代碼中有不少的 resolve
, 因此這裏的代碼使用resolved(被解決)
代替fulfilled
code
爲何沒有列出來更多的內容呢, 由於其它的內容大多和兼容性有關, 與這個實現原理關係不是太大, 還有的是到具體實現函數的時候纔會用到的規範, 因此我沒有列出來對象
注: catch
方法是ES6標準裏的, 它的原理是then(null, onRejected)
get
注: 本文代碼不考慮throw
, 爲了只體現原理, 讓代碼儘量更簡單.
Promise
的構造函數一般傳入一個執行者函數, 這個函數裏面多是異步邏輯(這麼說的意思就是也可能不是), 接受兩個參數: resolve
和reject
.
調用resolve(value)
就表明方法成功執行了, Promise
會把resolve
中傳入的value
傳給then
方法裏的參數
調用reject(reason)
就是執行出錯了, Promise
會把reject
中傳入的reason
傳給then
方法裏的參數
好, 下面開始作點準備工做
const Pending = 'pending' const Resolved = 'resolved' const Rejected = 'rejected' class MyPromise {}
誒, 這段代碼我感受不用解釋了吧? 下面的我會在註釋或者是代碼塊下方說明
class MyPromise { constructor(executor) { // 狀態 this.status = Pending // 正常運行返回的結果 this.value = null // 發生錯誤的緣由 this.reason = null // 詳見這段代碼塊下面寫的 注1 this.onRejected = () => {} this.onResolved = () => {} let resolve = value => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Resolved this.value = value this.onResolved(value) } let reject = reason => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason) } // 見 注2 executor(resolve, reject) } }
注1: 這是兩個被reject
或者resolve
後調用的回調函數, 我看的別人實現的版本大可能是一個數組, 而後調用的時候一個接一個調用裏面的函數.
我認爲對同一個promise調用屢次then
方法的時候不多, 並且本文只是一個思路展現, 並不嚴格遵照A+規範, 因此這裏就直接寫了個什麼也沒幹的函數
在這裏也分析一下, 在then
方法調用的時候, 若是調用then
時的狀態是Pending
, 那麼就設置一下當前對象裏的onRejected
和onResolved
, 具體設置什麼在後面的代碼裏會提到; 若是狀態不是Pending
, 就表明這兩個函數早就執行完了, 就須要根據this.value
和this.reason
具體的調用then
函數中傳進來的onRejected
和onResolved
.
注2: 這裏直接同步調用了, 沒有異步調用. 由於若是這個操做真的須要異步的話, 在executor
函數裏面就會有異步方法了(如setTimeout
), 不須要Promise
類給它辦.
而後就是then
方法啦~
注意: then
方法要求每次返回新的Promise
對象.
先寫個框架
then(onResolved, onRejected) { let funcOrNull = f => typeof f === "function" ? f : null onResolved = funcOrNull(onResolved) onRejected = funcOrNull(onRejected) if (this.status === Rejected) { return new MyPromise((resolve, reject) => { }) } else if (this.status === Resolved) { return new MyPromise((resolve, reject) => { }) } else { return new MyPromise((resolve, reject) => { }) } }
若是是狀態是rejected, 那麼
if (this.status === Rejected) { return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } }) }
這些實現的代碼包括下面的elseif和else塊就是最難理解的了, 我當時是很久很久也沒有理解, 接下來我會就像數學裏面同樣分類討論:
理解了Rejected塊, 那麼Resolved塊和他幾乎如出一轍, 只是函數名字不同而已, 因此我這裏會分析的儘量詳細
若是調用的時候是這樣的:
new MyPromise((resolve, reject) => { reject("I rejected the promise") }).then(null, console.log)
先分析構造方法, 建立Promise
對象的時候, 這裏它的狀態就變成Rejected
, 可是其餘的什麼事都沒幹, 讓咱們來看前面的代碼
this.onRejected = () => {} this.onResolved = () => {} let reject = reason => { if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason) } executor(resolve, reject)
這個時候this.onRejected
仍是個空函數, 因此調用它也沒什麼用
接下來到then
方法了, 讓咱們來看上面if塊裏的代碼
return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } })
能夠看出它執行了let value = onRejected(reason)
, 而後調用resolve(value)
, 以後這個新的Promise
狀態就是Resolved
了.
至於爲何這裏要用resolve
, 我是經過NodeJS作了個實驗看看NodeJS對這件事是怎麼幹的, 代碼以下
let p1 = new Promise((resolve, reject) => { reject("I rejected the promise") }) let p2 = p1.then(null, reason => { return 'I am from onRejected function' }) // 這裏是爲了避免管究竟是什麼狀態都能把p1和p2輸出出來 p2.then(() => console.log(p1, p2), () => console.log(p1, p2))
輸出(本來的執行結果沒有換行, 我爲了方便看本身加上的)
Promise { <rejected> 'I rejected the promise' } Promise { 'I am from onRejected function' }
這就看出來NodeJS是在處理完錯誤以後把onRejected
的返回值用resolve
函數處理了
若是調用的時候是這樣的
new MyPromise((resolve, reject) => { reject("I just rejected the promise") }).then(null, null).then(null, console.log)
這個時候就要考慮不能把錯誤信息丟掉了, 爲了實現這個"穿透"功能, 咱們能夠研究一下NodeJS是怎麼幹的
let p1 = new Promise((resolve, reject) => { reject("I rejected the promise") }).then(null, null) p1.then(() => console.log(p1), () => console.log(p1))
輸出
Promise { <rejected> 'I rejected the promise' }
這就很簡單了, NodeJS是把新的Promise
對象繼續調用reject
而且傳遞錯誤信息. 因此再看上面if塊裏的代碼
return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } })
能夠看出這裏也是在onRejected
空的時候直接用reject
方法把新的Promise
對象的狀態設置成了Rejected
而且也把this.reason
錯誤信息傳了過去.
展示成代碼的話, 就是執行了reject(this.reason)
你可能會疑惑, 那麼reject(this.reason)
返回值應該是undefined
, 而後又調用了resolve(value)
是怎麼回事呢?
這裏咱們要看前面的代碼
let resolve = value => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Resolved this.value = value this.onResolved(value) }
在調用完reject
以後, 這裏的status
就變成了Rejected
, 這個方法就不會調用了呀
你可能還會疑惑, 這裏的代碼
if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) }
雖說你知道返回值是Promise
要得出結果, 但這是onRejected
返回的值, 爲何第二行要這麼寫?
仍是老方法, 咱們看看NodeJS這個地方怎麼實現的
let p = new Promise((resolve, reject) => { reject("I rejected the promise") }).then(null, reason => { return new Promise((resolve, reject) => { resolve("Hello~") }) }).then(value => { console.log("Value " + value) }, reason => { console.log("Reason " + reason) })
運行結果
Value Hello~
因此說, 這裏須要這麼寫, 讓這個then
裏返回的Promise
對象then
方法的onResolved
方法直接調用新對象的resolve
和reject
方法來操做這個新對象
若是上面的都能理解了, 那麼下面這個elseif
塊就特別好理解了
else if (this.status === Resolved) { return new MyPromise((resolve, reject) => { let value = (onResolved === null ? resolve : onResolved)(this.value) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } } }
在else
塊裏, 也就是狀態是Pending
的時候, 須要作的事情幾乎和上面的if
和elseif
塊同樣
在Promise
對象狀態是Pending
的時候, 不能經過this.value
和this.reason
獲取值, 可是, 咱們能夠經過設置this.onRejected
和this.onResolved
這兩個函數, 由於當Promise
的executor
執行完的時候必定會調用這兩個函數中的一個, 而且調用它們的時候都會帶上value
和reason
, 因此這裏的代碼須要這麼寫
else { return new MyPromise((resolve, reject) => { this.onResolved = value => { let v = (onResolved === null ? resolve : onResolved)(value) if (v instanceof MyPromise) { v.then(resolve, reject) } else { resolve(v) } } this.onRejected = reason => { let v = (onRejected === null ? reject : onRejected)(reason) if (v instanceof MyPromise) { v.then(resolve, reject) } else { resolve(v) } } }) }
最後加上一個catch
方法, 其實就是一個語法糖, 既然ES6都加上了, 那我也加上吧
catch(onRejected) { return this.then(null, onRejected) }
嘿咻, 終於弄完了, 接下來就是實驗新對象的時候啦!(這麼說好像有點怪怪的呢)
仍是文章開頭那熟悉的味道
function getAnInt() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve(81) }, 500) }) } function sqrt(n) { return new MyPromise((resolve, reject) => { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500) }) } getAnInt().then(v1 => { console.log(v1) return sqrt(v1) }).then(v2 => { console.log(v2) return sqrt(v2) }).then(v3 => { console.log(v3) return sqrt(v3) }).then(v4 => { console.log(v4) }).catch(err => { console.log("Error " + err) })
結果
81 9 3 Error cannot get an int
附: 全代碼
const Pending = 'pending' const Resolved = 'resolved' const Rejected = 'rejected' class MyPromise { constructor(executor) { // 狀態 this.status = Pending // 正常運行返回的結果 this.value = null // 發生錯誤的緣由 this.reason = null // 見 注1 this.onRejected = () => {} this.onResolved = () => {} let resolve = value => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Resolved this.value = value this.onResolved(value) } let reject = reason => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason) } // 見 注2 executor(resolve, reject) } then(onResolved, onRejected) { let funcOrNull = f => typeof f === "function" ? f : null onResolved = funcOrNull(onResolved) onRejected = funcOrNull(onRejected) if (this.status === Rejected) { return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } }) } else if (this.status === Resolved) { return new MyPromise((resolve, reject) => { let value = (onResolved === null ? resolve : onResolved)(this.value) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } }) } else { return new MyPromise((resolve, reject) => { this.onResolved = value => { let v = (onResolved === null ? resolve : onResolved)(value) if (v instanceof MyPromise) { v.then(resolve, reject) } else { resolve(v) } } this.onRejected = reason => { let v = (onRejected === null ? reject : onRejected)(reason) if (v instanceof MyPromise) { v.then(resolve, reject) } else { resolve(v) } } }) } } catch(onRejected) { return this.then(null, onRejected) } } // 測試模塊! function getAnInt() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve(81) }, 500) }) } function sqrt(n) { return new MyPromise((resolve, reject) => { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500) }) } getAnInt().then(v1 => { console.log(v1) return sqrt(v1) }).then(v2 => { console.log(v2) return sqrt(v2) }).then(v3 => { console.log(v3) return sqrt(v3) }).then(v4 => { console.log(v4) }).catch(err => { console.log("Error " + err) })
參考: https://zhuanlan.zhihu.com/p/21834559
https://zhuanlan.zhihu.com/p/183801144