Promise為JavaScript的一個內建物件,為非同步操作提供了一個比起無限巢狀callbacks較易閱讀的解決方案,ES7的async/await也是以此為基礎發展,可謂是ES6的一個相當重要的語法
首先先看一下MDN的介紹來了解原理
今天要介紹以下物件或函式的基本寫法
- Promise的基本實現
- then()的基本實現
- then()的非同步操作
- then()的鏈式調用
- then()的穿透
- Promise的resolve().reject()的基本實現
- Promise.prototype.catch()的基本實現
- Promise的all().race()的基本實現
Promise的基本實現
依照MDN所說
一個 Promise
物件處於以下幾種狀態:
- 擱置(pending):初始狀態,不是 fulfilled 與 rejected。
- 實現(fulfilled):表示操作成功地完成。
- 拒絕(rejected):表示操作失敗了。
先定義狀態:
executor為一個依序接收兩個參數的函式:
resolve
及reject
(實現及拒絕回呼函式)。在 Promise 實作中,executor
函式在傳入參數resolve
與reject
後會立刻執行(executor
函式會在Promise
建構式回傳 Promise 物件前被執行)…
所以Promise應具備:value:儲存成功的值,reason:儲存失敗的理由,resolve函式:處理成功的結果,reject函式:處理Error,以及紀錄狀態的status
根據以上所述來試著寫建構函式
then()的基本實現
一個處於擱置狀態的 promise 能以一個值被實現(fulfilled),或是以一個原因或錯誤而被拒絕(rejected)。當上述任一狀態轉換發生時,那些透過
then
方法所繫結(associated)的處理函式列隊就會依序被調用……由於
Promise.prototype.then()
以及Promise.prototype.catch()
方法都回傳 promise…
then()是Promise的prototype上的方法
當狀態是成功時調用onFulfilled方法,失敗時則調用onRejected方法
來稍微測試一下剛才寫的東西
然而以上還未接觸到Promise的核心:非同步操作
then()的非同步操作
那些透過
then
方法所繫結(associated)的處理函式列隊就會依序被調用……
這裡是說要先把callbacks存起來,等到非同步執行完畢後再依序調用
接著來測試
then()的鏈式調用
接著是promise.then(…).then(…)….這種鏈式調用的實現,這時then必須return一個新的Promise
(因為Promise的prototype上才有then())
(還有Promise狀態不可以再次改變,所以不能return原本的Promise)
取得後要先做些處理(else裡的改寫法也跟if裡的相同)
首先要判斷返回的promise跟current是不是一樣的東西,如果是一樣的東西會造成無限循環調用,這時要直接以reject(Error)的方式處理
再來看returned的型態是不是object或function(只有這兩種型態才有可能內藏then()函式,這裡有種稱呼法:thenable),如果不是的話,就直接將結果resolve,如果是的話往下進行:
if裡面主要目的就是以循環調用resolvePromise的方式把promise全部跑完(拆掉?),其中調用then時要特別注意需要用到call來指定this,如果不使用call的情況下直接調用then(),這時this的指向就會是window(全域),而非我們剛剛處理到一半的Promise,這樣就會使得我們無法獲取該Promise的狀態
這裡還有一:resolve跟reject只能擇一被調用,如果都被調用或多次調用則只先跑最初被調用的,於是我們還要設個變數isCalled用來判斷是否已被調用
方法是在可能被調用的地方都先設個if判斷isCalled的值,如果是true直接return,而當第一次被調用時則將isCalled的值變更為true,這裡要做這種檢查的地方有三個:
第一跟第二自然是then.call(…)裡的resolve跟reject
第三個是try調用之後catch裡也有可能有這種多次調用的問題
這時當然還是得測試一下
這邊還有一種用法
then()的穿透
p.then().then(…).then().then(…)
跑出來的東西跟沒使用無參數的then()相同,也就是沒給參數的then執行時東西會直接穿透過去
做法就是then被調用時判斷是否有傳入失敗或成功的callbacks,沒有的話就傳入上回失敗或成功的值
這裡還有一個小東西
2.2.4
onFulfilled
oronRejected
must not be called until the execution context stack contains only platform code.
如果是同步執行的話,用Promise時還得幫then()考慮ㄧ下,前面有沒有非同步事件,執行完了嗎?
這時為了確保正確的執行順序,then()要以非同步的方式執行
Promise的resolve().reject()的基本實現
所以寫起來就是
Promise.prototype.catch()的基本實現
MDN寫得滿清楚的,直接寫起來就是
Promise的all().race()的基本實現
還有比較常用的all()及racr()
Promise.all()
當資料儲存的次數跟傳進來陣列長度一樣時,才可以將結果resolve
Promise.race()
其實在Promises/A+已經將實作方法說的滿清楚的
不過有些地方並沒有講清楚這麼做的理由
於是想來介紹一下並順便釐清自己的觀念
完整的程式碼請看這邊
測試結果請看下篇
參考