在ECMAScript 6標準中,Promise被正式列爲規範,Promise,字面意思就是「許諾,承諾」,嘿,聽着是否是很浪漫的說?咱們來探究一下這個浪漫的Promise對象究竟是如何許下承諾,又是如何兌現TA的諾言的。git
下面咱們將經過一些簡單的例子,來一步一步的探究Promise的含義,更重要的是,用心體會Ta的優雅和浪漫(PS:讓程序猿體會浪漫,是否是難爲你們了)es6
在ES6標準中被定義爲一個構造函數
,那好咱們就先new
一個對象出來看看。github
var promise = new Promise(function(resolve,reject){ //一個異步操做 setTimeout(function(){ let foo = 1; console.log('執行完成'); resolve(foo); }, 2000); });
上面的代碼就能夠看做是Promise給咱們許下了一個承諾,不過,這特麼究竟是個啥意思呢?
構造函數Promise
接收一個回調函數
作參數,同時這個回調函數又接受2個function作參數,本例中分別起名叫resolve
和reject
,分別表明異步操做執行成功後的操做
,和異步操做執行失敗後的操做
。
ES6標準中規定一個 Promise的當前狀態必須爲如下三種狀態中的一種:數組
進行中(Pending
)promise
已完成(Resolved
或 Fulfilled
)瀏覽器
已失敗(Rejected
)異步
下表簡單總結了三種狀態所表明的含義:函數
名稱 | 含義 | 知足條件 |
---|---|---|
Pending | 進行中 | 操做正在執行,能夠切換爲Resolve或Rejected狀態 |
Resolved | 已完成 | 必須擁有一個不可變的終值,且不能夠切換到其餘狀態 |
Rejected | 已失敗 | 必須擁有一個不可變的終值,且不能夠切換到其餘狀態 |
當Promise的狀態由pending
狀態,經過resolve函數改變爲resolved
,或者經過reject函數改變爲rejected
後,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,即便再對Promise對象添加回調函數,也會當即獲得這個結果。學習
最重要的不是看Ta怎麼說,重要的是要看Ta怎麼作。既然許下了承諾,就必需要兌現承諾。
下面就要介紹Promise.prototype.then()
方法,來訪問其當前值、終值和據因。它的做用是爲Promise實例添加狀態改變時的回調函數
。then
方法接收2個參數,第一個參數是Resolved狀態的回調函數,第二個參數(可選)是Rejected狀態的回調函數。
廢話很少說直接上示例代碼:網站
function foo (a){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(a > 0){ resolve(a); }else { reject(a); } }, 2000); }); return promise; } foo(1).then(function(a){ console.log("success",a+1); },function(a){ console.log("error",a); });
上面的代碼中,簡單設定一個定時任務模擬異步操做,當a>0
時,認爲操做成功,則在then
方法中執行第一個回調函數,輸出success
;當傳入的值 a<0
時,就被認爲操失敗,則then
方法執行第二個回調函數,輸出error
。看到這裏,咱們可能會感受到Promise 和咱們以前用的回調函數有點類似,可是貌似Promise是用同步的寫法來處理異步的操做。簡單來說,Promise就是能把原來的回調寫法分離出來,在異步操做執行完後,用鏈式調用的方式執行回調函數。
咱們瞭解了以上的基本原理,之後碰到須要傳遞多個callback的狀況的時候,就可使用Promise實現鏈式調用,從而避免陷入回調地獄
中。
示例代碼:
function foo(a) { ++a; console.log(a); var promise = new Promise(function (resolve, reject) { setTimeout(function () { if (a > 0) { resolve(a); } else { reject(a); } }, 2000); }); return promise; } function foo2(a) { ++a; console.log(a); var promise = new Promise(function (resolve, reject) { setTimeout(function () { if (a > 1) { resolve(a); } else { reject(a); } }, 2000); }); return promise; } function foo3(a) { ++a; console.log(a); var promise = new Promise(function (resolve, reject) { setTimeout(function () { if (a > 2) { resolve(a); } else { reject(a); } }, 2000); }); return promise; } function foo4(a) { ++a; console.log(a); var promise = new Promise(function (resolve, reject) { setTimeout(function () { if (a > 3) { resolve(a); } else { reject(a); } }, 2000); }); return promise; } //鏈式調用各個處理函數 foo(1).then(function (a) { return foo2(a); }) .then(function (a) { return foo3(a); }) .then(function (a) { return foo4(a); })
並非全部的承諾都會被兌現,就好像小時候父母都說幫咱們存壓歲錢之後還給咱們同樣。Promise中也是如此,總會有咱們意想不到的差錯發生致使沒法按照預期的函數進行執行,
因此Promise提供了一個名爲catch
的方法幫咱們解決這個問題。Promise.prototype.catch()
方法有2個做用,第一個做用,就是和then
方法中的第二個參數做用相同,就是當狀態切換成rejected
時執行相應的操做,例如第一個例子中換個寫法:
foo(1) .then(function(a){ console.log("success",a+1); }) .catch(function(a){ console.log("error",a); });
做用和原來徹底一致。
第二個做用就是在執行resolve的回調(也就是上面then中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼並不會報錯卡死js,而是會進到這個catch方法中。
要是你在生活中真遇到一個肯爲你玩兒命的人。。。。。
關我毛事,勞資單身狗,秀恩愛的請移步他處。。。。。
Promise中提供了Promise.all
方法,注意all
方法是直接定義在Promise上的而不是原型鏈中。主要做用就是提供了並行執行異步操做的能力,讓程序玩了命的跑起來。
var promise = Promise.all([p1, p2, p3]).then(function (p) { console.log(p); });
Promise.all會將多個Promise的實例,包裝成一個新的Promise實例,而後傳遞給後面的then
方法。
Promise.all方法的參數能夠不是數組,但必須具備Iterator接口,且返回的每一個成員都是Promise實例
說了這麼多,哇塞這麼優雅的特性必定要用起來啊,可是在激動之餘,畢竟Promise是ES6標準中的新增對象,因此仍是得冷靜下來檢查一下Promise對象的兼容性如何,
經過在can I use...網站上獲取到的統計數據看來,Promise對象的兼容性不是很好,目前只有在比較新的主流瀏覽器中才獲得全面支持,並且咱們注意到微軟的全部IE瀏覽器均不支持該對象,只有在win10中的Edge瀏覽器中才得到支持。看來咱們在準備使用Promise的時候,仍是要視本身的實際開發環境而定。
關於ES6的其餘特性的最新支持狀況,請點擊這裏
Promise做爲ES6標準中一個比較新鮮的特性,還有其餘方法這裏沒有講到,好比resolve
,reject
,race
等,受限於水平與時間精力,今天暫時先寫這麼多,之後有時間再繼續補充,對於文中的錯誤和不足,歡迎你們指出討論,共同窗習進步(雖然我不必定看。。。。逃。。)