[JavaScript]一塊兒來實現一個Promise

本文僅做爲交流學習,主要爲juejiner個人總結複習之用,還望大佬們輕噴

前言

初學js踩過最多的坑是什麼,有人說是隱式轉換,有人說是this改變,而對我而言,是異步函數。有不少次,被卡在空數據報錯裏,因而學會了默認值賦值,學會了深層判空,學會了經過異步回調來同步和異步加載數據或者控制狀態的加載。javascript

而這裏,不可避免的就要學習es6所推崇的promise異步編程方案。上一篇文章《[JavaScript] Promise 與 Ajax/Axios》中我簡單介紹了promise的用法。那麼這裏咱們來深延一下promise,看看這方神聖到底能玩出什麼❀來~html

一 Promise

開始研究以前除了看看阮神的es6,紅皮書,和掘友的文章以外,固然得看看比較官方的實現以及相關規範了java

promise-polyfill
react

PromiseA+ios

whatgit

一個構造函數,其參數爲一個excutor,其構造出的對象擁有私有成員state,value,reactios...etces6

下面是一個簡單的Promise,不用細看掃一眼往下走接着看介紹github

// 格式掛了...
function Promise (excutor) {
 let _ = this
 _.state = 'pending'
 _.value = undefined
 _.fullfillArr = []
 _.rejectedArr = []
 let resolve = result => {
   if (this.state !== 'pending') return
   setTimeout(() => {
     this.state = 'fullfilled'
     this.value = result
     this.fullfillArr.forEach(fn => {
      fn()
     })
   }, 0)
 }
 let reject = reason => {
   if (this.state !== 'pending') return
   setTimeout(() => { // 同步時插入,保證回調函數被異步執行
     this.state = 'rejected'
     this.value = reason
     this.rejectedArr.forEach(fn => {
       fn()
    })
   }, 0)
 }
 try {
   excutor(resolve, reject)
 } catch (error) {
   reject(error)
 }
}   

能夠看到Promise中有編程

1.1 幾個私有成員

state數組

中存儲狀態值,有三種,實例化時的'pending',被resolve或reject函數調用時被決定爲fullfilled或rejected。

value

中存儲狀態改變函數resolvet所傳過來的值。

reason

中存儲狀態改變函數reject所傳來的值。

1.2 執行器函數excutor

主要做用是進行一些同步異步操做而後在異步的回調函數中調用狀態改變函數,或者是同步結束時調用狀態改變函數。固然你也能夠啥都不調用,這樣也就沒有後續被添進callbackArray中的大夥們啥事了。

excutor爲Promise的形參,其實參爲new Promise((resolve, reject) => { resolve(233) })中的(resolve, reject) => { resolve(233) },即一個定製的函數。

該函數有兩個參數: resolve, reject,兩個形參在excutor被調用時實參分別對應Promise的內部函數resolve, reject。

而resolve和reject內部函數且看下面

1.3 執行器函數的兩個參數

是兩個函數,做用是在將須要改變promise對象狀態時將其狀態改變,而且傳入一個所需的值賦值給promiseX.value。

在promise的excutor中被執行時的兩個參數被傳入Promise內部函數resolve, reject,在被調用時實參爲其傳來的值,如上慄中的233。

好了看完這些,你大概知道Promise和他的執行器都在幹嗎了,大概核心就是實例化一個對象,該對象擁有幾個擁有默認值的私有成員:狀態,值...等等,而後再執行一下excutor執行器函數,這個時候根據需求將對象的狀態值決定下來而且將某個傳來的值保存下來。

好了,大致就作了這些,那麼作了這些才只是開始呀,咱們用Promise不就是想代替回調地獄嗎,鏈式調用的時候怎麼實現,值怎麼傳過去,何時執行定義好的下一個步驟,這些看下一節核心方法:then的實現。

二 Promise.prototype.then

then是幹嗎的? 指定promise對象在決定態(onFullfilled, onRejected)時多執行的回調函數

因此then是一個函數,其形參有兩個,onFullfilled ,onRejected,第二個參數爲可選。其實參爲咱們實際定製時所定義的函數。

eg:

new Promise((resolve, reject) => {
 resolve('fullfilled')
})
.then(
(res) => {console.log(res)},
(reason) => {coonsole.log(reason)}
)複製代碼

上個栗子中的(res) => {console.log(res)}即爲onFullfilled對應的實參, (reason) => {coonsole.log(reason)}即爲onRejected對應的實參。它只是個函數的引用,因此不要看着一大串就覺得在執行,實際上這兩個函數並無在上述的同步代碼中執行,爲何呢,看下面

2.1 異步執行與同步then

爲了可以使同一個promise在狀態決定後去同步執行多個步驟,好比A動畫完了後要求加載B,C動畫,且BC動畫由於其後續場景不一樣的緣由不該該放置一個函數中,那麼就有了一個promise屢次,then同步執行的需求了。則異步執行就很重要了,須要在本次棧內代碼執行完多個then添加後再去進行狀態更改而且將該promise下的then所有執行。嘖嘖,發佈訂閱的感受吼。不同的是,錯事後該次改變後再添加一樣能獲得執行。

本文裏使用了setTimeout異步宏任務來實現異步,從而使狀態值更改時全部的then回調都已經添加到位從而被所有執行。

同時,promise對象中要有兩個回調函數數組fufillArr,rejectArr來儲存每一個then中的onfullFilled和onRejected,其添加(push)時機固然是在then函數中了。這裏就對應了本節第一句主題:指定promise對象在決定態(onFullfilled, onRejected)時多執行的回調函數。

2.2 then的鏈式

寫以前不妨先想一想,鏈式什麼意思,不就是在回調中再去調用一個回調的意思嗎,那麼在異步回調需求出現時,咱們不就又須要一個狀態機器Promise了麼,立刻給他來個return new Promise(....)操做。ok,這不就完事了。個錘子了。

好了你有了promise對象返回知道怎麼決定後續的狀態了,那你還麼得作上面的要求push呢,因而咱們將push操做放在了這個要new 的Peomise的excutor中,這樣的意義在於後續的回調決定於前一輪push進去的回調的執行結果。

到這裏,一個簡易的promise以及then的實現過程就說的差很少了固然中間又不少判斷以及普通值狀況判斷就不細說了一切皆在代碼中,那麼上一下then 的代碼。

// 格式又掛了,直接看下面github上代碼吧 
Promise.prototype.then = function (onfullFilled, onRejected) {                                 typeof onfullFilled !== 'function' ? onfullFilled = result => result : null;            typeof onRejected !== 'function' ? onRejected = reason => {                throw new Error(reason instanceof Error ? reason.message : reason);            } : null                                         return new Promise((resolve, reject) => {                                                this.fullfillArr.push(() => {                    try {                        let onfullFilledReturn = onfullFilled(this.value)                                                onfullFilledReturn instanceof Promise ? onfullFilledReturn.then(resolve, reject) : resolve(onfullFilledReturn)                    } catch (error) {                        reject(error)                    }                })                                                                       this.rejectedArr.push(() => {                    try {                        let onRejectedReturn = onRejected(this.value)                        onRejectedReturn instanceof Promise ? onRejectedReturn.then(resolve, reject) : resolve(onRejectedReturn)                    } catch (error) {                        reject(error)                    }                })                            })      }複製代碼

最後掛一下總體的實現以及驗證連接: promiseComeTrue.html

三 Promise.prototype.all and Promise.prototype.race

也是寫實現,不是很難,你們本身寫下吧,我明天再寫。困。。

參考

Promise 對象

promise-polyfill

PromiseA+

我如何實現Promise A+

扒一扒PROMISE的原理,你們不要怕!

Promise原理講解 && 實現一個Promise對象 (遵循Promise/A+規範)

相關文章
相關標籤/搜索