1. 程式人生 > >ES6之Promise學習與實踐

ES6之Promise學習與實踐

1.前言

  在平時的業務開發中,前端通常需要請求後臺獲取資料,或者NodeJs讀取檔案等等一系列的非同步操作,我們通常需要利用非同步操作的結果或者對非同步操作的結果進行處理。通常我們的解決方案是:在非同步操作成功或者失敗的回撥函式裡面寫方法,在非同步操作比較簡單的時候這樣寫程式碼還是比較好理解的,當業務逐漸趨於複雜,這就形成了回撥地獄,程式碼巢狀層數太多並且難以理解。不過,辦法總是有的,可以使用ES6的新特性Promise來解決問題。

2.Promise的定義

  Promise 是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。它由社群最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise物件。

所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise 是一個物件,從它可以獲取非同步操作的訊息。Promise 提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。

Promise物件有以下兩個特點。

(1)物件的狀態不受外界影響。Promise物件代表一個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise物件的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對Promise物件添加回調函式,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

有了Promise物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式。此外,Promise物件提供統一的介面,使得控制非同步操作更加容易。

Promise也有一些缺點。首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。其次,如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部。第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

3.基本用法

  ES6規定,Promise物件是一個建構函式,用來生成Promise例項

程式碼如下:

const promise=new Promise(function(resolve,reject){
  if(/* 非同步操作成功*/){
    resolve(value)
  }else{
    reject(error);
  }
});

Promise建構函式接受一個函式作為引數,改函式的兩個引數分別是resolve與reject,resolve函式的作用是:將Promsie物件的狀態從“未完成”變為“成功”,在非同步操作成功時回撥,並將非同步操作的結果value作為引數傳遞出去;reject函式的作用是:Promise物件的狀態由“未完成”變為“失敗”,在非同步操作失敗時呼叫,並將非同步操作報出的錯誤作為引數傳遞出去。

Promise例項生成以後,可以用then方法分別制定resolved的狀態和reject狀態的回撥函式。

程式碼如下:

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

4.小例子-封裝Ajax操作

 const getJSON = function(url) {
    const promise = new Promise(function(resolve, reject){
      const handler = function() {
        if (this.readyState !== 4) {
          return;
        }
        if (this.status === 200) {
          resolve(this.response);
        } else {
          reject(new Error(this.statusText));
        }
      };
      const client = new XMLHttpRequest();
      client.open("GET", url);
      client.onreadystatechange = handler;
      client.responseType = "json";
      client.setRequestHeader("Accept", "application/json");
      client.send();

    });

    return promise;
  };

  getJSON("./js/package.json").then(function(json) {
    console.log('Contents: ' , json);
  }, function(error) {
    console.error('出錯了', error);
  });

以上的程式碼是通過用原生的Ajax和Promise實現的獲取json的一個方法,在網頁中執行得出以下的結果:

5.reject狀態回撥方法的寫法

promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

以上是我們的reject狀態方法,可以作為promsie物件then方法的第二個引數。不過這樣寫有個缺點是:如果在resolve狀態之後再丟擲錯誤,則不會捕獲。

推薦寫法:

// 推薦寫法
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });

  通過Promise這種寫法,我們使很多非同步操作的方法寫法同步化,能夠更好的組織優化程式碼,而且在捕獲錯誤也更加容易,方便我們除錯解決問題。不足之處,多多指正。


 

參考資料:

 《ECMAScript 6 入門》-阮一峰

《MDN-Promise》