JavaScript Promises 初體驗

Promise 是什麼?

Promise 對象用來進行延遲(deferred) 和 異步(asynchronous) 計算。html

一個 Promise 處於如下三種狀態之一:html5

  • pending: 初始狀態, 非 fulfilled 或 rejected.
  • fulfilled: 成功的操做.
  • rejected: 失敗的操做.

Promise 接口表示爲一個值的代理,這個值在promise建立時未必已知. 它容許你將 handlers 與一個異步 action 最終的成功或失敗狀態關聯起來. 這使得異步方法能夠像同步方法那樣返回值: 異步方法返回一個在將來某個時刻擁有一個值的 promise 來替代最終返回值.node

pending狀態的promise既可帶着一個值成爲 fulfilled 狀態,也可帶着一個reason成爲 rejected 狀態. 任意狀況發生時, 經過promise的 then 方法所排列(queued up)的相應handlers 就會被調用. (當綁定相應的 handler 時,若是 promise 已經處於 fulfilled 或 rejected 狀態這個 handler 將會被調用, 因此在異步操做的完成和它的 handler 的綁定之間不存在競爭條件.)jquery

由於 Promise.prototype.then 和 Promise.prototype.catch 方法返回 promises, 因此它們能夠被鏈式調用—一種被稱爲 composition 的操做.git

promises.png

Promise 爲什麼出現?

簡單的說,Promise 被當成 callback hell 的救命仙丹。es6

回調函數是JavaScript的一大特點! node.js官方的api基本都是以會回調方式傳遞函數返回值。習慣同步編程的對這種異步方式多少會產生水土不服,並且層層嵌套,不知不覺就建造起了一座高高的回調金字塔。針對這種廣泛問題,Promise應勢而生!github

Promise 基本用法

建立 Promise編程

var promise = new Promise(function(resolve, reject) {
  // 作一些異步操做的事情,而後……

  if (/* 一切正常 */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

Promise 的構造器接受一個函數做爲參數,它會傳遞給這個回調函數兩個變量 resolve 和 reject。在回調函數中作一些異步操做,成功以後調用 resolve,不然調用 reject。json

調用 reject 的時候傳遞給它一個 Error 對象只是個慣例並不是必須,這和經典 JavaScript 中的 throw 同樣。傳遞 Error 對象的好處是它包含了調用堆棧,在調試的時候會有點用處。windows

使用 Promise:

promise.then(function(result) {
  console.log(result); // 「完美!」
}, function(err) {
  console.log(err); // Error: "出問題了"
});

「then」接受兩個參數,成功的時候調用一個,失敗的時候調用另外一個,兩個都是可選的,因此你能夠只處理成功的狀況或者失敗的狀況。

Promise 實踐

  1. 值的處理

你能夠對結果作些修改而後返回一個新值:

var promise = new Promise(function(resolve, reject) {
  resolve(1);
});

promise.then(function(val) {
  console.log(val); // 1
  return val + 2;
}).then(function(val) {
  console.log(val); // 3
});
  1. 隊列的異步操做

你也能夠把「then」串聯起來依次執行異步操做。

當你從「then」的回調函數返回的時候,這裏有點小魔法。若是你返回一個值,它就會被傳給下一個「then」的回調;而若是你返回一個「類 Promise」的對象,則下一個「then」就會等待這個 Promise 明確結束(成功/失敗)纔會執行。例如:

你也能夠把「then」串聯起來依次執行異步操做。

當你從「then」的回調函數返回的時候,這裏有點小魔法。若是你返回一個值,它就會被傳給下一個「then」的回調;而若是你返回一個「類 Promise」的對象,則下一個「then」就會等待這個 Promise 明確結束(成功/失敗)纔會執行。例如:

返回一個值

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  console.log("Got chapter 1!", chapter1);
});

你返回一個「類 Promise」的對象

var storyPromise;

function getChapter(i) {
  storyPromise = storyPromise || getJSON('story.json');

  return storyPromise.then(function(story) {
    return getJSON(story.chapterUrls[i]);
  })
}

// 用起來很是簡單:
getChapter(0).then(function(chapter) {
  console.log(chapter);
  return getChapter(1);
}).then(function(chapter) {
  console.log(chapter);
});
  1. 錯誤處理

前面已經看到,「then」接受兩個參數,一個處理成功,一個處理失敗(或者說確定和否認,按 Promise 術語):

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.log("Failed!", error);
});

你還可使用「catch」:

get('story.json').then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.log("Failed!", error);
});

Promise 被否認以後會跳轉到以後第一個配置了否認回調的 then(或 catch,同樣的)。對於 then(func1, func2) 來講,必會調用 func1 或 func2 之一,但毫不會兩個都調用。而 then(func1).catch(func2) 這樣,若是 func1 返回否認的話 func2 也會被調用,由於他們是鏈式調用中獨立的兩個步驟。看下面這段代碼:

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
});

Promise API 參考

靜態方法

Promise.resolve(promise);
返回一個 Promise(當且僅當 promise.constructor == Promise)

Promise.resolve(thenable);
從 thenable 對象建立一個新的 Promise。一個 thenable(類 Promise)對象是一個帶有「then」方法的對象。

Promise.resolve(obj);
建立一個以 obj 爲確定結果的 Promise。

Promise.reject(obj);
建立一個以 obj 爲否認結果的 Promise。爲了一致性和調試便利(如堆棧追蹤),obj 應該是一個 Error 實例對象。

Promise.all(array);
建立一個 Promise,當且僅當傳入數組中的全部 Promise 都確定以後才確定,若是遇到數組中的任何一個 Promise 以否認結束,則拋出否認結果。每一個數組元素都會首先通過 Promise.resolve,因此數組能夠包含類 Promise 對象或者其餘對象。確定結果是一個數組,包含傳入數組中每一個 Promise 的確定結果(且保持順序);否認結果是傳入數組中第一個遇到的否認結果。

Promise.race(array);
建立一個 Promise,當數組中的任意對象確定時將其結果做爲確定結束,或者當數組中任意對象否認時將其結果做爲否認結束。

Promise 的現行解決方案

其實已經有一些第三方庫實現了 Promise 的功能:

上面這些庫和 JavaScript 原生 Promise 都遵照一個通用的、標準化的規範:Promises/A+,jQuery 有個相似的方法叫 Deferred,但不兼容 Promises/A+ 規範,因而會有點小問題,使用需謹慎。jQuery 還有一個 Promise 類型,它實際上是 Deferred 的縮減版,因此也有一樣問題。

目前的瀏覽器已經(部分)實現了 Promise。

Chrome 3二、Opera 19 和 Firefox 29 以上的版本已經默認支持 Promise。因爲是在 WebKit 內核中因此咱們有理由期待下個版本的 Safari 也會支持,而且 IE 也在不斷的開發中。

要在這兩個瀏覽器上達到兼容標準 Promise,或者在其餘瀏覽器以及 Node.js 中使用 Promise,能夠看看這個 polyfill(gzip 以後 2K)。

相關參考

相關文章
相關標籤/搜索