同步更新部落格: www.cnblogs.com/GerryOfZhon…
同步更新專欄: zhuanlan.zhihu.com/zhongqiang
同步更新github: github.com/GerryIsWarr…
半年不迭代,迭代搞半年,說的就是我,這裡有點尷尬了,直接進入主題吧
我記得在這篇部落格的時候整合了Promise的,不過那個時候就簡簡單單的寫了一點最基礎,在一些特殊的case上,還是有點問題的,所以才有了這個部落格。在拜讀了w3c和PromiseA+規範之後,從頭到尾詳細的瞭解了Promise這個東西,然後自己親手寫了一個和es6文件擁有相同功能的庫。
什麼是promise?
promise是一個物件,表示單個非同步操作的最終結果。
什麼時候使用?
任何一門技術都不是一個“萬金油”,只有在它最合適的場地出現,才是實現它最大價值的地方,so,Promise一樣逃不過這個“真香”定律。
-
one-and-done操作模型
- 非同步I / O操作:從儲存API讀取或寫入的方法可以返回承諾。
- 非同步網路操作:通過網路傳送或接收資料的方法可以返回承諾。
- 長時間執行的計算:需要一段時間來計算某些東西的方法可以在另一個執行緒上完成工作,返回結果的承諾。
- 使用者介面提示:要求使用者回答的方法可以返回承諾。
-
One-Time "Events”模型
即使在已經履行或被拒絕之後也可以訂閱,當某些事情只發生一次,並且作者經常想要在它已經發生之後觀察它的狀態
-
更多狀態的變化
影象,字型等資源的載入loaded屬性,這個屬性僅在資源完全載入是才會實現,否則拒絕
不建議使用promise的場景
-
任何一個可能不止一次發生的事件,都不是one-and-done模型的
-
大的流資料,分步處理流資料,而無需將流的全部內容緩衝到記憶體中。
Tips
這裡有個w3c組織搞出來的一個指南--Writing Promise-Using Specifications,建議大家拜讀一下,雖然沒有詳細講解規範,但是對於Promise使用場景和特性做了一次詳細的介紹,包括我上面說的使用場景等等。
以上講的更多偏向應用層的知識點,下面就讓我們深入它的根本去了解Promise是如何實現的。
Promise本身是一種社群規範--Promises/A+,由Promise A+組織進行制定,它們提供了一個大綱和指導性的方案,只要能實現其所列規範,都可以視作實現了Promise A+,so,後面的各種變種啊,什麼樣的功能,都可以根據自己所需去設計,但是基礎的方案按照A+ 組織規範實現就可以。
這個大綱比較囉嗦、枯燥、無味,so,我們靠3張我畫的圖去理解一下Promise
第一張:promise整體流程圖
-
例項化Promise的時候(定義內部狀態的初始值等等),在同步狀態下,首先會執行初始化程式碼,比如:new Promise((res,rej)=>{ console.log('這裡就是初始化程式碼') }),然後再執行then方法。這個執行順序在promise下是錯誤的,因為在例項程式碼中會首先改變Promise狀態,但是前置的callback還沒有在then方法中注入,所以要做推遲例項程式碼(setTimeout,可以將任務推到執行週期之後,巨集任務),讓then先跑起來,注入狀態變更需要的前置依賴。
-
在Promise規範中定義了,then方法,必須返回一個Promise,so,Promise2就是then的返回值。然後then的動作就是將所有需要前置依賴的回撥函式,Promise狀態,狀態變化的value全都儲存起來。
-
等待推遲的例項程式碼(官方叫:非同步)執行之後,觸發了Promise狀態的變化這個動作,然後去改變內部定義的狀態,以及狀態變化所要執行的操作。
-
內部狀態已經變化完成,但是return的Promise2狀態還是pending,所以我們需要將自身的Promise和Promise2的狀態進行同步以及是否可then的持續操作。
以上為Promise的整體流程思路,它就是這樣跑起來的。不過知道這個流程以後,還是一知半解的,下面我們就對核心方法then進行詳細剖析。
第二張:then方法核心解析
then方法需要分3個狀態去解析
- pending 狀態
a. 首先例項化執行then方法,這個時候初始化的內部狀態都是pending,這個時候,我們要做一個訂閱和釋出的設計,將then傳入的resolve和reject的回撥進行包裝和儲存,並訂閱觸發動作。(這邊的包裝是因為訂閱的時候,不僅僅只是執行回撥函式,還需要處理promise2的狀態同步問題)
b. 在等待出發的狀態的時候,這個時候狀態沒有變更,所以還是keep pending狀態,而promise2也是pending;當promise觸發了resolve,這個時候就需要處理之前訂閱的回撥了,先改變Promise自身的狀態,然後呼叫callback,將callback的值傳入解析函式,同步改變Promise2的狀態;觸發reject動作和resolve一樣
c. 這樣,在整個pending鏈路上,自身狀態和promise2狀態全都同步改變完成
- resolve 狀態
a. Promise內部狀態以及變更完畢,內部會儲存PromiseValue的值,直接獲取PromiseValue的值作為引數,調起then方法傳進來的resolveCallback的函式。
b. 使用同步解析函式,去同步改變Promise2的狀態,以及後續可then的操作
- reject 狀態
該狀態操作,同resolve操作,只是變更狀態不一樣
以上為then方法的所有操作流程,pending的時候最特殊,有個訂閱釋出設計來改變自身狀態,然後同步改變Promise2的狀態。其他resolve和reject,都是狀態已經變更完畢,直接取狀態變更的值,處理回撥,然後同步改變Promise2的狀態值。這邊同步變更Promise2的規則,在A+的規範裡是有定義的。
第三張:同步改變Promise2狀態以及是否可繼續then的操作
狀態同步改變和是否可持續then的操作解析流程,都按照A+規範去判斷-
x代表callback的返回值,首先判斷x和Promise2是否相等,相等丟擲TypeError的錯誤(畢竟如果返回值x和Promise2是一個物件的話,那操作就沒啥意義了)
-
判斷x是否是Promise物件,如果是的話,說明x是可支援then的,然後根據x的狀態進行操作和同步改變Promise2的值,pending就等待執行結束,resolve和reject就分別改變Promise2的狀態
-
如果x不是Promise物件,判斷x是否是物件或者function,否的話直接resolve Promise2狀態。如果是的話,try-catch捕獲,定義then = x.then是否報錯,如果報錯則reject掉Promise2狀態。
-
判斷then是否是function,如果是則執行then操作,如果then方法執行了reject,則reject掉Promise2。
-
如果then執行resolve,則將y替換掉x,重新和Promise2進行狀態的同步改變。
PS:這裡的x.then就是下面的這種狀況,A+規範定義這樣的返回值代表還是可then的,需要處理
temp.then(function (x) {
return {
then: function(resolve, reject) {
resolve(42);
}
}
})
複製程式碼
至此整個Promise就結束了,從Promise怎麼去執行,到核心程式碼then的處理,以及Promise2的同步改變,這就是所謂的Promise。回過頭來發現,最值得佩服的是這種規範的設計思維,通過一種設計思維,將簡單的技術化腐朽為神奇。所以,個人意見,程式設計師的進階,最重要的不是程式碼的熟練度,而是思維的進階。熟練度這個是每個人都可以靠時間堆積出來的,但是更高階的工程師,應該能從整體的視角去了解,然後規劃和設計,將簡單的技術化神奇,將複雜的問題化簡單。
程式碼可以到 github 上檢視,功能完善了,支援all、race、resolve、reject方法
在ajax-js的庫的變動,如下:
-
替換之前有問題的createPromise的程式碼
-
將get、post、postForm,obtainBlob,upload進行改造,改方法返回都是Promise(考慮這些都是one-and-done模型,而輪詢和大檔案切割上傳2個方法是持續性操作,所以不做改變)
-
刪除postJSON、promiseAjax方法
ajax-js庫增加新功能:mock功能
全域性配置引數:
// mock功能
mock: {
isOpen: true,
mockData: {}
},
複製程式碼
流程如下:
demo:
// 全域性配置
ajax.config({
baseURL:'http://localhost:3000/',
mock: {
isOpen:true,
mockData: {
'post':'我是mock資料'
}
}
})
// 測試程式碼
function request_post() {
ajax.post('post',{data:'ajaxPost'})
.then(x=>{
console.warn(x)
})
}
複製程式碼
測試結果:
注意:mockData的key是url的值,不是baseUrl+url的值
結束語:
ajax-js.1.9.2完成了,一直在思考還有什麼需要改進的東西,之後的迭代需要走的方向
-
完成http其他協議,put、delete等等
-
npm的包面向現代化,去除各種polyfill和一些相容程式碼
-
配置webpack自動打包壓縮
-
探索通訊和其他技術的結合玩法
-
等等...