談談 ES6 的 Promise 對象

https://segmentfault.com/a/1190000002928371

前言

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

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

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

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

  1. 在須要多個操做的時候,會致使多個回調函數嵌套,致使代碼不夠直觀,就是常說的 Callback Hell。ajax

  2. 若是幾個異步操做之間並無先後順序之分(例如不須要前一個請求的結果做爲後一個請求的參數)時,一樣須要等待上一個操做完成再實行下一個操做。segmentfault

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

什麼是 Promise

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

resolve 和 reject

首先來看一段使用了 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 對象的狀態改變爲失敗,同時將錯誤的信息傳遞到後續錯誤處理的操做。

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 對象進行開發,在 Node.js 配合 babel 也能夠很方便地使用。

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

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

相關文章
相關標籤/搜索