開篇首先設想一個平常開發經常會遇到的需求:在多個接口異步請求數據,而後利用這些數據來進行一系列的操做。通常會這樣去寫:javascript
$.ajax({
url: '......', success: function (data) { $.ajax({ // 要在第一個請求成功後才能夠執行下一步 url: '......', success: function (data) { // ...... } }); } });
這樣的寫法的原理是,當執行一些異步操做時,咱們須要知道操做是否已經完成,全部當執行完成的時候會返回一個回調函數,表示操做已經完成。java
使用回調函數的形式理解起來並不困難,可是實際的應用當中會有如下的缺點:jquery
在須要多個操做的時候,會致使多個回調函數嵌套,致使代碼不夠直觀,就是常說的 Callback Hell。ajax
若是幾個異步操做之間並無先後順序之分(例如不須要前一個請求的結果做爲後一個請求的參數)時,一樣須要等待上一個操做完成再實行下一個操做。segmentfault
爲了解決上述的問題,Promise 對象應運而生,在 EMCAScript 2015 當中已經成爲標準。api
一個 Promise 對象能夠理解爲一次將要執行的操做(經常被用於異步操做),使用了 Promise 對象以後能夠用一種鏈式調用的方式來組織代碼,讓代碼更加直觀。並且因爲 Promise.all
這樣的方法存在,可讓同時執行多個操做變得簡單。接下來就來簡單介紹 Promise 對象。數組
首先來看一段使用了 Promise 對象的代碼。瀏覽器
function helloWorld (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!"); } else { reject("Good bye!"); } }); } helloWorld(true).then(function (message) { alert(message); }, function (error) { alert(error); });
jsFiddle(請用現代瀏覽器打開)babel
上面的代碼實現的功能很是簡單,helloWord
函數接受一個參數,若是爲 true
就打印 "Hello World!",若是爲 false
就打印錯誤的信息。helloWord
函數返回的是一個 Promise 對象。異步
在 Promise 對象當中有兩個重要方法————resolve
和 reject
。
resolve
方法可使 Promise 對象的狀態改變成成功,同時傳遞一個參數用於後續成功後的操做,在這個例子當中就是 Hello World!字符串。
reject
方法則是將 Promise 對象的狀態改變爲失敗,同時將錯誤的信息傳遞到後續錯誤處理的操做。
上面提到了 resolve
和 reject
能夠改變 Promise 對象的狀態,那麼它究竟有哪些狀態呢?
Promise 對象有三種狀態:
Fulfilled 能夠理解爲成功的狀態
Rejected 能夠理解爲失敗的狀態
Pending 既不是 Fulfilld 也不是 Rejected 的狀態,能夠理解爲 Promise 對象實例建立時候的初始狀態
helloWorld 的例子中的 then
方法就是根據 Promise 對象的狀態來肯定執行的操做,resolve 時執行第一個函數(onFulfilled),reject 時執行第二個函數(onRejected)。
helloWorld 的例子當中利用了 then(onFulfilld, onRejected)
方法來執行一個任務打印 "Hello World!",在多個任務的狀況下then
方法一樣能夠用一個清晰的方式完成。
function printHello (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello"); } else { reject("Good bye!"); } }); } function printWorld () { alert("World"); } function printExclamation () { alert("!"); } printHello(true) .then(function(message){ alert(message); }) .then(printWorld) .then(printExclamation);
上述例子經過鏈式調用的方式,按順序打印出了相應的內容。then
可使用鏈式調用的寫法緣由在於,每一次執行該方法時老是會返回一個 Promise 對象。另外,在 then
onFulfilled 的函數當中的返回值,能夠做爲後續操做的參數,所以上面的例子也能夠寫成:
printHello(true).then(function (message) { return message; }).then(function (message) { return message + ' World'; }).then(function (message) { return message + '!'; }).then(function (message) { alert(message); });
一樣能夠打印出正確的內容。
catch
方法是 then(onFulfilled, onRejected)
方法當中 onRejected
函數的一個簡單的寫法,也就是說能夠寫成then(fn).catch(fn)
,至關於 then(fn).then(null, fn)
。使用 catch
的寫法比通常的寫法更加清晰明確。
Promise.all
能夠接收一個元素爲 Promise 對象的數組做爲參數,當這個數組裏面全部的 Promise 對象都變爲 resolve 時,該方法纔會返回。
var p1 = new Promise(function (resolve) { setTimeout(function () { resolve("Hello"); }, 3000); }); var p2 = new Promise(function (resolve) { setTimeout(function () { resolve("World"); }, 1000); }); Promise.all([p1, p2]).then(function (result) { console.log(result); // ["Hello", "World"] });
上面的例子模擬了傳輸兩個數據須要不一樣的時長,雖然 p2 的速度比 p1 要快,可是 Promise.all
方法會按照數組裏面的順序將結果返回。
平常開發中常常會遇到這樣的需求,在不一樣的接口請求數據而後拼合成本身所需的數據,一般這些接口之間沒有關聯(例如不須要前一個接口的數據做爲後一個接口的參數),這個時候 Promise.all
方法就能夠派上用場了。
還有一個和 Promise.all
相相似的方法 Promise.race
,它一樣接收一個數組,不一樣的是隻要該數組中的 Promise 對象的狀態發生變化(不管是 resolve 仍是 reject)該方法都會返回。
最後是關於 Promise 對象的兼容性問題。
在瀏覽器端,一些主流的瀏覽器都已經可使用 Promise 對象進行開發,在 Node.js 配合 babel 也能夠很方便地使用。
若是要兼容舊的瀏覽器,建議能夠尋找一些第三方的解決方案,例如 jQuery 的 $.Deferred。
感謝你的閱讀,有不足之處請爲我指出。