快速將 Promise 運用在開發中

FREAKFILTH發表於2017-03-12

這篇文章面向對Promise不甚瞭解的朋友,我將告訴你如何把它快速運用在開發中。

什麼是Promise?

簡單幾句介紹一下。Promise是抽象非同步處理物件以及對其進行各種操作的元件。你可以理解為:它的出現,是為了讓我們更方便的進行非同步處理。

在Promise出現之前,說到JavaScript的非同步處理,我們都會想到回撥函式,like this:

getAsync("fileA.txt", function(error, result){
  if(error){// 取得失敗時的處理 throw error;
    throw error;
  }
});複製程式碼

上面遵循Node.js的規定,回撥的第一個引數是error。如果所有的回撥函式都像Node.js一樣,統一引數使用規則的話,那寫法會很明瞭,但也僅僅是編碼規範而已,使用不同的寫法也不會出錯。

而Promise則是把非同步處理物件和處理規則進行規範化,並採用統一的介面來編寫,使用規定方法之外的寫法都會出錯。

我們可以先看一個簡單的使用Promise進行非同步處理的例子:

var promise = getAsyncPromise("fileA.txt");

promise.then(function(result){
  // 獲取檔案內容成功時的處理
}).catch(function(error){
  // 獲取檔案內容失敗時的處理
});複製程式碼

看上去和回撥函式有些不一樣,在使用Promise進行非同步處理的時候,我們必須按照介面規定的方法編寫處理程式碼。

也即是說,除了使用Promise規定的方法(上面的thencatch),其他的方法都是不能使用的,而回撥函式可以自定義回撥的引數。

所以,Promise可以將複雜的非同步處理輕鬆的進行模式化,沒有理由讓你不使用它。

接下來,我們看看怎麼把Promise運用到開發中,這個才是大家想了解的。

學習Promise

在運用到開發之前,我們有必要先學習一些Promise的基本API(暫時看的有點糊塗沒關係,等會的例子實踐會和大家講清楚的)。

目前大致有下面三種型別:

1.Constructor(構造器)

我們從建構函式Promise來建立一個新promise物件作為介面。

要建立一個promise物件,可以使用new來呼叫Promise構造器來進行例項化。

var promise = new Promise(function(resolve, reject) { // 非同步處理
  // 處理結束後、呼叫resolve 或 reject
});複製程式碼

2.Instance Method(例項方法)

在通過new生成的promise物件時,我們設定了在resolve(成功)和reject(失敗)時呼叫的回撥函式,我們可以使用promise.then()例項方法。

promise.then(onFulfilled,onReject);複製程式碼
  • resolve(成功)時:onFulfilled會被呼叫

  • reject(失敗)時:onReject會被呼叫

onFulfilledonReject都為可選引數

promise.then成功和失敗時都可以使用,另外在異常處理時,可以使用promise.then(undefined, onReject)這種方式,只指定reject時的回撥函式即可。不過這種情況下,使用promise.catch()是個明智之選。

promise.catch(onReject);複製程式碼

3.Static Method(靜態方法)

promise.all()Promise.resolve()等在內,主要都是一些輔助方法(可以理解為一些語法糖),這裡不作深入探討。

運用在開發中

我們先來看一段Promise使用流程程式碼:

function asyncFunction() {  //(1)
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Async Hello World');
    }, 300);
  });
}

asyncFunction().then(function(value) {  //(2)
  console.log(value); //300ms後列印 "Async Hello World"
}).catch(function(error) {  //(3)
  console.log(error);
})複製程式碼

分析一下上面程式碼。

執行(1)處函式,會返回一個Promise物件,Promise物件內部在300ms後執行resolve()方法,這個方法呼叫(2)處的then()方法,並傳入引數,如果Promise物件內部出現任何錯誤(比如平臺不支援setTimeout方法),就會執行(3)處的catch()發放,並把錯誤作為引數傳入。

這裡提一下,我看到很多朋友把Promise理解為Ajax的一種擴充套件,其實並不是這樣的,Ajax只是一種請求資料的方式,因為Ajax是非同步的,所以我們可以用Promise去管理Ajax請求,但這並不意味這Promise只服務於Ajax,只要是非同步處理,我們都可以使用Promise去處理,就比如上面的setTimeout

看到這裡大家對Promise應該有一個大概的認識了,實際開發中Promise大部分時間還是搭配Ajax使用,我們來看看應該怎麼做,下面用原生的方式請求Ajax,大家也溫習一下:

function getURL(URL) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();

    req.open('GET', URL, true);

    req.onload = function() {
      if (req.status === 200) {
        resolve(req.responseText);
      }else {
        reject(new Error(req.statusText));
      }
    };

    req.onerror = function() {
      reject.(new Error(req.statusText));
    };

    req.send();
  });
}

// 執行示例
var URL = "https://rockjins.js.org";
getURL(URL).then(function onFulfilled(value){
  console.log(value);
}).catch(function onRejected(error){
  console.error(error);
});複製程式碼

getURL只有在XHR取得狀態為200時才會呼叫resolve,也就是資料取得成功時,而其他情況(資料取得失敗)則會呼叫reject

當呼叫resolve(req.responseText)時,then方法也會被呼叫,並接收到req.responseText引數。

熟悉Node.js的朋友在寫回撥時會會將callback(error,response)的第一個引數設為error物件,在Promise中,resolve(成功)/reject(失敗)擔當了這個職責。

XHR中onerror觸發時,就是發生錯誤時,理所當然要呼叫reject,我們重點來看下傳給reject的值。

發生錯誤時要像這樣reject(new Error(req.statusText)),建立一個Error物件再講具體的值傳入進去。傳給reject的值也沒有什麼特殊限制,一般只要是Error物件(或繼承自Error物件)即可。

小結

其實你理解了Promise的運作流程,使用它十分方便和簡單,它就是一個非同步管理器,幫助我們更好地去進行非同步處理。

試想,如果Promise真的很複雜,那它出現的意義是什麼?本末倒置了,哈哈。

喜歡本文的朋友可以關注我的微信公眾號,不定期推送一些好文。

快速將 Promise 運用在開發中

本文出自Rockjins Blog,轉載請與作者聯絡。否則將追究法律責任。

相關文章