ES6 中 Promise物件使用學習

2023-01-11 12:00:47

轉載請註明出處:

  Promise 物件是 JavaScript 的非同步操作解決方案,為非同步操作提供統一介面。它起到代理作用(proxy),充當非同步操作與回撥函數之間的中介,使得非同步操作具備同步操作的介面。Promise 可以讓非同步操作寫起來,就像在寫同步操作的流程,而不必一層層地巢狀回撥函數。

promise是一種非同步解決方案。

  由於ajax非同步方式請求資料時,我們不能知道資料具體回來的事件,所以過去只能將一個callback函數傳遞給ajax封裝的方法,當ajax非同步請求完成時,執行callback函數。
  promise物件接受resolve和reject兩個引數,當一個非同步動作發生時,promise物件會通過resolve完成對動作成功進行解析,reject會捕獲這個動作的異常。一個promise物件,通過new Promise().then()執行下一步驟操作。
  axios is a promise based HTTP client for the browser and node.js。也就是說,使用axios發出請求,難免涉及promise
  Promise建構函式的引數是一個函數,函數裡面的程式碼是非同步的,即Promise裡面的操作,和Promise()外面的操作時非同步"同時"進行的。Promise中的函數的第一個引數是回撥函數,resolve用來觸發then裡面的程式碼,第二個引數是回撥函數,reject用來觸發catch中的程式碼,throw new Error();也可以觸發catch,

  • resolve和reject是兩個回撥函數,呼叫resolve會觸發then,reject會觸發catch
<script>
new Promise((resolve, reject) =>{
    setTimeout(() =>{
        //成功的時候呼叫resolve
        resolve('成功data')
        //失敗的時候呼叫reject
        reject('error message')
    }, 1000)
}).then((data) =>{
    //處理成功後的邏輯
    console.log(data);//這個data 是接收的resolve引數--
}).catch((err) =>{
    console.log(err);
})
</script>  

Promise 鏈式程式設計方式比較

  傳統的鏈式呼叫程式設計如下:

// 傳統寫法
step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // ...
      });
    });
  });
});
 

  使用 Promise 之後,可以簡化為如下:

// Promise 的寫法
(new Promise(step1))
  .then(step2)
  .then(step3)
  .then(step4);

  從上面程式碼可以看到,採用 Promises 以後,程式流程變得非常清楚,十分易讀。注意,為了便於理解,上面程式碼的Promise範例的生成格式,做了簡化

Promise 物件的狀態

  Promise 物件通過自身的狀態,來控制非同步操作。Promise 範例具有三種狀態。

    非同步操作未完成(pending)
    非同步操作成功(fulfilled)
    非同步操作失敗(rejected)
  上面三種狀態裡面,fulfilled和rejected合在一起稱為resolved(已定型)。

  這三種的狀態的變化途徑只有兩種。

    從「未完成」到「成功」
    從「未完成」到「失敗」
  一旦狀態發生變化,就凝固了,不會再有新的狀態變化。這也是 Promise 這個名字的由來,它的英語意思是「承諾」,一旦承諾成效,就不得再改變了。這也意味著,Promise 範例的狀態變化只可能發生一次。

  因此,Promise 的最終結果只有兩種。

  非同步操作成功,Promise 範例傳回一個值(value),狀態變為fulfilled。
  非同步操作失敗,Promise 範例丟擲一個錯誤(error),狀態變為rejected。

Promise 建構函式

  JavaScript 提供原生的Promise建構函式,用來生成 Promise 範例。

var promise = new Promise(function (resolve, reject) {
  // ...
 
  if (/* 非同步操作成功 */){
    resolve(value);
  } else { /* 非同步操作失敗 */
    reject(new Error());
  }
});

  上面程式碼中,Promise建構函式接受一個函數作為引數,該函數的兩個引數分別是resolve和reject。它們是兩個函數,由 JavaScript 引擎提供,不用自己實現。

  resolve函數的作用是,將Promise範例的狀態從「未完成」變為「成功」(即從pending變為fulfilled),在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去。reject函數的作用是,將Promise範例的狀態從「未完成」變為「失敗」(即從pending變為rejected),在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。
  下面是一個例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}
 
timeout(100)

  上面程式碼中,timeout(100)返回一個 Promise 範例。100毫秒以後,該範例的狀態會變為fulfilled

Promise.prototype.then()

   Promise 範例的then方法,用來新增回撥函數。

  then方法可以接受兩個回撥函數,第一個是非同步操作成功時(變為fulfilled狀態)時的回撥函數,第二個是非同步操作失敗(變為rejected)時的回撥函數(該引數可以省略)。一旦狀態改變,就呼叫相應的回撥函數。

var p1 = new Promise(function (resolve, reject) {
  resolve('成功');
});
p1.then(console.log, console.error);
// "成功"
 
var p2 = new Promise(function (resolve, reject) {
  reject(new Error('失敗'));
});
p2.then(console.log, console.error);
// Error: 失敗

  上面程式碼中,p1和p2都是Promise 範例,它們的then方法系結兩個回撥函數:成功時的回撥函數console.log,失敗時的回撥函數console.error(可以省略)。p1的狀態變為成功,p2的狀態變為失敗,對應的回撥函數會收到非同步操作傳回的值,然後在控制檯輸出。

  then方法可以鏈式使用。

p1.then(step1)
  .then(step2)
  .then(step3)
  .then(
    console.log,
    console.error
  );

  上面程式碼中,p1後面有四個then,意味依次有四個回撥函數。只要前一步的狀態變為fulfilled,就會依次執行緊跟在後面的回撥函數。

  最後一個then方法,回撥函數是console.log和console.error,用法上有一點重要的區別。console.log只顯示step3的返回值,而console.error可以顯示p1、step1、step2、step3之中任意一個發生的錯誤。舉例來說,如果step1的狀態變為rejected,那麼step2和step3都不會執行了(因為它們是resolved的回撥函數)。Promise 開始尋找,接下來第一個為rejected的回撥函數,在上面程式碼中是console.error。這就是說,Promise 物件的報錯具有傳遞性。

小結  

  Promise 的優點在於,讓回撥函數變成了規範的鏈式寫法,程式流程可以看得很清楚。它有一整套介面,可以實現許多強大的功能,比如同時執行多個非同步操作,等到它們的狀態都改變以後,再執行一個回撥函數;再比如,為多個回撥函數中丟擲的錯誤,統一指定處理方法等等。

  而且,Promise 還有一個傳統寫法沒有的好處:它的狀態一旦改變,無論何時查詢,都能得到這個狀態。這意味著,無論何時為 Promise 範例新增回撥函數,該函數都能正確執行。所以,你不用擔心是否錯過了某個事件或訊號。如果是傳統寫法,通過監聽事件來執行回撥函數,一旦錯過了事件,再新增回撥函數是不會執行的。

  Promise 的缺點是,編寫的難度比傳統寫法高,而且閱讀程式碼也不是一眼可以看懂。你只會看到一堆then,必須自己在then的回撥函數裡面理清邏輯。

ES6 學習連結

  https://es6.ruanyifeng.com/#docs/promise