ES6 Promise非同步程式設計

程式_人生發表於2020-10-28

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

所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。

從語法上說,Promise是一個物件,從它可以獲取非同步操作的訊息。Promise提供統一的API,各種非同步操作都可以用同樣的方法進行處理。

Promise物件有以下兩個特點。

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

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

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

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

// 基本用法
// ES6規定,Promise物件是一個建構函式,用來生成Promise例項
 let promise = new Promise((resolve, reject) => {
  // ... some code of post a api(or http) request and waiting for the response
  if (/* 非同步操作成功 */){
    resolve(value)
  } else {
    reject(error)
  }
})

Promise.prototype.then()

// Promise例項生成以後,可以用then方法分別指定Resolved狀態和Reject狀態的回撥函式。
// then方法可以接受兩個回撥函式作為引數。第一個回撥函式是Promise物件的狀態變為Resolved時呼叫,第二個回撥函式是Promise物件的狀態變為Reject時呼叫。
// 其中,第二個函式是可選的,不一定要提供。這兩個函式都接受Promise物件傳出的值作為引數。
promise
	.then((value) => {
	  // success callback handle
	}, (error) => {
	  // failure callback handle
})

Promise.prototype.catch()
此方法是.then(null, rejection)的別名,用於指定發生錯誤時的回撥函式。

let promise = getJSON("/posts.json").then((posts) => {
  // 如果請求失敗
  reject(new Error('fail')) // new 一個錯誤物件會被catch() 捕獲
}).catch((error) => {
  // 處理 getJSON 和 前一個回撥函式執行時發生的錯誤
  // promise丟擲一個錯誤,就被catch方法指定的回撥函式捕獲
  console.log('發生錯誤!', error);
});

一般來說,不要在then方法裡面定義Reject狀態的回撥函式(即then的第二個引數),總是使用catch方法。

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

// good
promise
  .then((data) => { //cb
    // success
  })
  .catch((err) => {
    // error
  });
// 理由是第二種寫法可以捕獲前面then方法執行中的錯誤,也更接近同步的寫法(try/catch)。
// 因此,建議總是使用catch方法,而不使用then方法的第二個引數

相關文章