1. Promise的含義
Promise
是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。它由社群最早提出和實現,ES6
將其寫進了語言標準,統一了用法,原生提供了Promise
物件。
所謂Promise
,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise
是一個物件,從它可以獲取非同步操作的訊息。Promise
提供統一的 API
,各種非同步操作都可以用同樣的方法進行處理。
Promise物件有以下兩個特點
物件的狀態不受外界影響。
Promise
物件代表一個非同步操作,有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise
這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。一旦狀態改變,就不會再變,任何時候都可以得到這個結果。
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)fulfilled
:promise
非同步操作成功完成
(2)rejected
:由於程式錯誤或者其他的一些原因,promise
非同步操作未能成功完成,即已失敗。
一旦promise
狀態改變,就不會再變,任何時候都可以得到這個結果。promise
物件的狀態改變,只有兩種可能:從pending
變為fulfilled
和從pending
變為rejected
。
在promise
狀態改變後,我們怎麼去根據不同的狀態來做相應的處理呢?promise
物件有一個then()
方法,它接受2個引數:第一個是當promise
的狀態變為fulfilled
時呼叫的函式,與非同步操作相關的附加資料通過呼叫resolve
函式產地給這個完成函式;第二個是當promise
的狀態變為rejected
時要呼叫的函式,所有與失敗相關的附加資料通過呼叫rejected
函式傳遞個這個拒絕函式。新增promise
的then()
方法的呼叫,程式碼如下:
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);
})