談談 ES6 的 Promise 對象

前言

開篇首先設想一個平常開發經常會遇到的需求:在多個接口異步請求數據,而後利用這些數據來進行一系列的操做。通常會這樣去寫:jquery

     
     
     
     
$.ajax({
url: '......',
success: function (data) {
$.ajax({
// 要在第一個請求成功後才能夠執行下一步
url: '......',
success: function (data) {
// ......
}
});
}
});

這樣的寫法的原理是,當執行一些異步操做時,咱們須要知道操做是否已經完成,全部當執行完成的時候會返回一個回調函數,表示操做已經完成。es6

使用回調函數的形式理解起來並不困難,可是實際的應用當中會有如下的缺點:ajax

  1. 在須要多個操做的時候,會致使多個回調函數嵌套,致使代碼不夠直觀,就是常說的 Callback Hell
  2. 若是幾個異步操做之間並無先後順序之分(例如不須要前一個請求的結果做爲後一個請求的參數)時,一樣須要等待上一個操做完成再實行下一個操做。

爲了解決上述的問題,Promise 對象應運而生,在 EMCAScript 2015 當中已經成爲標準。api

什麼是 Promise

一個 Promise 對象能夠理解爲一次將要執行的操做(經常被用於異步操做),使用了 Promise 對象以後能夠用一種鏈式調用的方式來組織代碼,讓代碼更加直觀。並且因爲 Promise.all 這樣的方法存在,可讓同時執行多個操做變得簡單。接下來就來簡單介紹 Promise 對象。數組

resolve 和 reject

首先來看一段使用了 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(請用現代瀏覽器打開)瀏覽器

上面的代碼實現的功能很是簡單,helloWord 函數接受一個參數,若是爲 true 就打印 「Hello World!」,若是爲 false 就打印錯誤的信息。helloWord 函數返回的是一個 Promise 對象。babel

在 Promise 對象當中有兩個重要方法————resolve 和 reject異步

resolve 方法可使 Promise 對象的狀態改變成成功,同時傳遞一個參數用於後續成功後的操做,在這個例子當中就是 Hello World! 字符串。函數

reject 方法則是將 Promise 對象的狀態改變爲失敗,同時將錯誤的信息傳遞到後續錯誤處理的操做。

Promise 的三種狀態

上面提到了 resolve 和 reject 能夠改變 Promise 對象的狀態,那麼它究竟有哪些狀態呢?

Promise 對象有三種狀態:

  • Fulfilled 能夠理解爲成功的狀態
  • Rejected 能夠理解爲失敗的狀態
  • Pending 既不是 Fulfilld 也不是 Rejected 的狀態,能夠理解爲 Promise 對象實例建立時候的初始狀態

helloWorld 的例子中的 then 方法就是根據 Promise 對象的狀態來肯定執行的操做,resolve 時執行第一個函數(onFulfilled),reject 時執行第二個函數(onRejected)。

then 和 catch

then

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);

jsFiddle

上述例子經過鏈式調用的方式,按順序打印出了相應的內容。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);
});

jsFiddle

一樣能夠打印出正確的內容。

catch

catch 方法是 then(onFulfilled, onRejected) 方法當中 onRejected 函數的一個簡單的寫法,也就是說能夠寫成 then(fn).catch(fn),至關於 then(fn).then(null, fn)。使用 catch 的寫法比通常的寫法更加清晰明確。

Promise.all 和 Promise.race

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 兼容性

在瀏覽器端,一些主流的瀏覽器都已經可使用 Promise 對象進行開發,在 Node.js 配合 babel 也能夠很方便地使用。

若是要兼容舊的瀏覽器,建議能夠尋找一些第三方的解決方案,例如 jQuery 的 $.Deferred

感謝你的閱讀,有不足之處請爲我指出。



詳細參考:http://es6.ruanyifeng.com/#docs/promise

相關文章
相關標籤/搜索