1. Promise的含義

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

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

Promise物件有以下兩個特點

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

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

注意,為了行文方便,本章後面的resolved統一隻指fulfilled狀態,不包含rejected狀態。

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

Promise的缺點

Promise也有一些缺點。

首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。

其次,如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部。

第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

如果某些事件不斷地反覆發生,一般來說,使用 Stream 模式是比部署Promise更好的選擇。

2.基本用法

一個promise可以通過promise建構函式來建立,這個建構函式只接受一個引數:包含初始化promise程式碼的執行器(executor)函式,在該函式內包含需要非同步執行的程式碼。執行器函式接受兩個引數,分別是resolve函式和reject函式,這兩個函式由JavaScript引擎提供,不需要我們自己編寫。非同步操作結束成功時呼叫resolve函式,失敗時呼叫reject函式。

示例程式碼如下:

const promise = new Promise((resolve, reject) => {
// 開啟非同步操作
setTimeout(function() {
try {
let c = 6 / 2
// 執行成功時呼叫resolve函式
resolve(c)
} catch (ex) {
// 執行失敗時呼叫reject函式
reject(ex)
}
}, 1000)
})

  在執行器函式內包含了非同步呼叫,在1s後執行兩個數的除法運算,如果成功,則用相除的結果作為引數呼叫resolve函式,失敗則呼叫reject函式。

  每個promise都會經歷一個短暫的生命週期:先是出於進行中(pending)的狀態,此時操作尚未完成,所以它是未處理的,一旦非同步操作執行結束,promise則變為已處理的狀態。操作結束後,根據非同步操作執行成功與否,可以進入以下兩個狀態之一:

  (1)fulfilledpromise非同步操作成功完成

  (2)rejected:由於程式錯誤或者其他的一些原因,promise非同步操作未能成功完成,即已失敗。

  一旦promise狀態改變,就不會再變,任何時候都可以得到這個結果。promise物件的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected

  在promise狀態改變後,我們怎麼去根據不同的狀態來做相應的處理呢?promise物件有一個then()方法,它接受2個引數:第一個是當promise的狀態變為fulfilled時呼叫的函式,與非同步操作相關的附加資料通過呼叫resolve函式產地給這個完成函式;第二個是當promise的狀態變為rejected時要呼叫的函式,所有與失敗相關的附加資料通過呼叫rejected函式傳遞個這個拒絕函式。新增promisethen()方法的呼叫,程式碼如下:

promise.then(value => {
console.log(value); // 3
}, err => {
console.error(err.message);
})

  then()方法的兩個引數都是可選的。例如,只在執行失敗後進行處理,可以給then()方法的第一個引數出傳遞null。程式碼如下:

promise.then(null, err => {
console.error(err.message);
})

  promise物件還有一個catch()方法,用於在執行失敗後進行處理,等價於上述只給then()方法傳入拒絕處理函式的程式碼,如下:

promise.catch(err => {
console.error(err.message)
})

  但是通常我們是將then()方法和catch()方法一起使用來對非同步操作的結果進行處理,這樣能更清楚的指明操作結果是成功還是失敗,程式碼如下:

promise.then(value => {
// 完成
console.log(value);
}).catch(err => {
// 拒絕
console.error(err.message);
})