ESCMScript6(3)Promise對象
- 2021 年 7 月 23 日
- 筆記
- ESCMScript6
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);
})

