文 | Leigh,UPYUN 已得到受權
原文連接:http://t.cn/R403hc4javascript
在 JavaScript 這麼多年發展中,尤爲在前端領域框架層出不窮,解決方案也琳琅滿目,Promise 這個思想也逐漸從一個框架層面的實現變成了 ES 的規範,而且愈來愈多的新 API 都在以 Promise 爲基礎制定。是時候來看看這個怪物了!前端
在 JavaScript 尤爲是前端開發領域,Promise 已經存在很多時日了,有一些曾經廣受好評的第三方庫,例如 Q,when,Bluebird,RSVP,基本都曾名聞遐邇,儘管實現方式/API 不同,但它們大多都遵循着 Promises/A+ 這個‘事實規範’。而歸入 ES 規範的 Promise API 也是創建在 Promises/A+ 基礎上的。java
要注意的是,jQuery 雖然有一個相似 Promise 的 API,可是它的 Promise 其實是被稱做爲 Deferred 對象的一個東西,它跟 Promises/A+ 的規範是不太兼容。git
Promise 主要用來解決 JS 開發中的異步問題,而對異步的處理,社區中也有着很多的解決方案,最多的就是回調這種形式,好比,在 Node.js 中:es6
這種書寫上的約定,很容易解決對異步操做的處理,但缺陷是,這樣的寫法,咱們沒發直接在異步函數中進行 return 和 throw 操做,只能侷限於回調的形式中,而且很容易引發回調金字塔的狀況。Promise 則對異步處理和處理方法都作了規範和抽象,還給了開發者在異步代碼中使用 return 和 throw 的能力。而這也是 Promise 存在的真正意義。繼續閱讀 domenic 的相關解釋github
其中 executor 函數擁有兩個參數 resolve 和 reject。resolve 用於確定 Promise,reject 用於否認它,咱們能夠在相關的操做結束後來執行這兩個函數。web
能夠看到,在語法上看,Promise 仍是有點像回調函數那種形式的,囧。不過,和回調函數相比,它的主要不一樣點在於:編程
一、每個 Promise 都保證能讓咱們收到一個值,Promise 即是這個值的代理,而咱們無需關心它被建立的時間點,咱們更能夠在任意時刻註冊咱們的結果處理函數,即便這個 Promise 已經完成(resolved)了(這時候,所註冊的處理函數會被當即執行)。api
二、每個 Promise 都只會被成功或失敗一次,而且這個狀態不會被改變。數組
一個 Promise,會擁有如下幾個狀態之一:
一、pending(等待):操做正在執行中。
二、fulfilled(完成):操做已經成功執行完畢。
三、reject(失敗):操做執行失敗。
某些文章中可能會說到 settled 這個詞,settled 表明 Promise 不是 pending 的狀態,即:要麼是 fulfilled,要麼是 reject。但他自己並非一個狀態,只是爲了說的時候方便。詳見 domenic: States and Fates
Promise 的狀態變化圖以下:
建立
建立一個 Promise,直接的方法是直接 new Promise 建立一個,即上文中語法中所述同樣。其接受一個 executor 函數做爲參數傳遞,Promise 在 executor 函數中提供了 resolve 和 reject 兩個函數供開發者根據實際結果來調用。
在 Promise 對象上,還有 Promise.all(iterable), Promise.race(iterable), Promise.reject(reason), Promise.resolve(value) 四個方法,這些方法也會返回 Promise 對象,所以,咱們也能夠經過這些方法來直接建立一個方法,例如 var p = Promise.resolve('I am a Promise!');
消費
Promise 一旦建立,咱們就能夠把它當作一個值來傳遞,由於 Promise 就是對一個將來會獲得的值的表明。所以,你能夠將它從函數中返回(return),也能夠當作參數來傳遞到須要的地方,就像傳遞一個普通的值同樣。
消費 Promise,即對 Promise 的所表明的值進行一系列的處理。這裏,咱們可使用 .then() 方法。
.then() 方法能夠接受兩個函數做爲參數,第一個參數的函數會在 Promise 完成(fulfilled)的時候被調用,而第二個參數的函數則在 Promise 失敗 (rejected)的時候調用。這兩個參數都是能夠被省略的:
每一個 Promise 實例還有一個 .catch() 方法,主要用於對錯誤的處理,對於 .catch() 的方法理解,其實很簡單,它就至關於 .then() 省略第一個參數:
上述兩種形式對錯誤的處理是等效的,當 Promise 失敗的時候,兩段代碼的執行結果同樣。不過,通常狀況下,更加推薦使用 .catch() 方法來處理錯誤,由於 catch 自己名字更加直接易懂,而且,在鏈式調用 Promise 的時候,只須要在調用鏈最末端使用一個 .catch() 便可。
在 Promise 中,當直接 throw 一個異常的時候,將會使該 Promise 置爲 rejected 狀態,這裏須要注意的是,在 Promise 鏈中拋出異常,僅僅會將拋出異常所在的 Promise 置爲 rejected,而不會影響原始的 Promise,好比:
處理多個異步操做
Promise.all()
在不少時候,咱們都但願多個異步操做可以並行,但不少狀況下,咱們又但願能在這多個異步操做所有都完成後再對結果進行處理,這個時候,咱們就會用到 Promise.all(),這個方法接受一個 Promise 數組做爲參數,且會在全部 Promise 完成後執行一次回調 .then(),在這裏,全部數組中的 Promise 都是並行方式執行的,以下:
注意:只要一旦有一個 promise 失敗,.catch() 中的邏輯就會被執行。
Promise.all() 在一些大型 web app 中,將會頗有用,由於大多良好的 API 設計都是以數據維度劃分,產品的某一個功能,可能會涉及到多個 API 的數據聯動,這時候,使用 Promise.all() 便可方便的解決。
Promise.race() 它一樣接受一個 Promise 數組做爲參數,顧名思義,'race' 即比賽,想象一下,在平時百米競賽時候,一旦有人率先衝過終點,那變產生了一個冠軍,Promise.race() 便是在數組中的任意一個 Promise 完成後,便會觸發 .then() 的邏輯,固然,這裏數組中的 Promise 也是並行的:
Promise.race() 的經典使用場景是某個服務有多個互備的形式,而咱們須要儘快的拿到結果。
Series
並行很容易,可是 Promise 並無一個直接使用的串行的方法,固然,結合一些函數式編程的方法,咱們能夠很簡單的自行實現一下:
Promise 最近變得愈來愈火,不管前端領域,例如:Fetch API,Battery API,以及還未完成的 ServiceWorker API 等,甚至在 Node.js 社區中,人氣很高的 co,以及 koa 新版本都將 thunk 的形式改成了 Promise,在實際生產中投入使用,阻力也不會太大。
ES 規範
MDN 文檔
Can I Use 兼容性參考
es6-promise polyfill
歡迎關注 UPYUN 微信公衆號( ID : upaiyun ),咱們每週都會分享高質量的原創技術文章。