JavaScript
理解 Promise 狀態及使用方式
2017/12/14 15:31:15
0
758
理解 Promise 狀態及使用方式
簡介 |
當今 HTTP Client 套件 ( axios, fetch ... ) 多數已支援 Promise 標準,開發人員可透過 Promise 物件掌控非同步作業的執行狀態,依據 Promise 狀態決定成功 / 失敗時所需執行的行為;本篇文章針對 ES6 Promise 物件的操作方式進行介紹,對 Promise 有初步的認識後,在操作上比較不會迷失在 then 來 then 去的 promise chain 中。 |
作者 |
陳志澤 |
前言
當今作為 HTTP Client 套件 ( axios, fetch ... ) 多數已支援 Promise 標準,開發人員可透過 Promise 物件掌控非同步作業的執行狀態,依據 Promise 狀態決定成功 / 失敗時所需執行的行為,在語法使用上較傳統 call back 方式清晰好理解。本篇文章針對 ES6 Promise 物件的操作方式進行介紹,對 Promise 有初步的認識後,在操作上比較不會迷失在 then 來 then 去的 promise chain 中。
什麼是 Promise ?
Promise 是 ES6 訂定的新標準物件,但其實這也不是什麼新鮮事,因為早在訂定此標準前,已有許多函式庫或框架實作出相同概念的做法;
Promise 語法結構可滿足開發者解決非同步操作的需求,透過此流程語法結構,提供一個比較方便的操作方式來調用非同步作業,使用 Promise 表示一個非同步作業的最終結果,在語法使用上較傳統 call back 方式清晰好理解。
ES6 Promise 支援性
建立 Promise 物件
建立 Promise 方式如下
var promise = new Promise ( (resolve, reject) => { ... } )
- 在建構函式傳入 executor 函式
- 可在函式中執行非同步作業
- 當作業順利完成後:
- 呼叫 resolve(value) 回應所得資料
- value 通常是合法的 javascript 值
- 會進入 Fulfilled (已實現) 狀態
- 當作業發生錯誤時:
- 呼叫 reject(reason) 回應錯誤資訊
- reason 通常會式 Error 物件
- 會進入 Rejected (已拒絕) 狀態
範例如下:
function doSomethingAsync(){
return new Promise((resolve, reject) => {
// 模擬非同步作業(ex. call api)
setTimeout(function () {
let isSuccess = false
if (isSuccess) {
// 成功(資料 value 向下一個連鎖傳遞)
resolve('success')
} else {
// 失敗(錯誤 reason 向下一個連鎖傳遞)
reject(new Error('something wrong'))
}
}, 2000)
})
}
依據 promise 狀態來決定處置方式
可使用 then 獲得最終處理結果及進行接續行為
var newPromise = promise.then( onFulfilled, onRejected )
- 傳入 onFulfilled 方法來定義作業成功時的處理方式
- 傳入 onRejected 方法來定義作業失敗時的處理方式
- 執行後會另外產生新的 Promise 物件
(因此可以使用 chained 結構繼續操作 promise)
onFulfilled = (value) => {...}
- 在 Promise 的 executor 中呼叫 resolve 方法會進入此方法中
( 狀態為 Fulfilled已實現時 ) - 傳入值:
- 由上層 executor 透過 resolved(value) 傳入的資料
- 或從上層 onFulfilled 或 onRejected 方法直接 return value
- 不區分值是從 onFulfilled 還是 onRejected 傳出來 (因在 onFulfilled 或 onRejected 不發生錯誤 / 不呼叫 reject 時,都會進入下個 onFulfilled 中,當然也包括回傳值)
- 回傳值:
- value 值 (可以從新Promise 物件 then 中 onFulfilled 方法參數中取得)
- Promise 物件
- 當有需要再處理一個非同步作業時使用
- 自己建立一個 Promise 取代 then 回傳的新 Promise 物件
- thenable 物件
onRejected = (reason) => {...}
- 在 Promise 的 executor 中呼叫 reject方法會進入此方法中
( 狀態為 Rejected已拒絕時 ) - 傳入值:
- 由上層 executor 透過 reject(reason) 傳入的錯誤發生資訊
- 或從上層 onFulfilled 或 onRejected 方法直接呼叫 Promise.reject(reason)
- 回傳值:也是可以回傳 value, promise 或 thenable 物件
- 多數使用情境下在 then 中只會定義 onFulfilled 方法 (會忽略此部分)
- 錯誤多會交由 promse.catch 作攔截處置
範例如下:
// promise 成功要執行的方法
function onFulfilled (value) {
console.log(value) // 'success'
}
// promise 失敗要執行的方法
function onRejected (reason) {
console.log(reason.message) // 'something wrong'
}
// then 就是處置 promise 成功或失敗要做的事情
doSomethingAsync()
.then(onFulfilled, onRejected)
// 通常不特別在 then 中定義 onRejected 方法
// 多數使用 catch 直接捕捉/處理上方連鎖發生的錯誤
doSomethingAsync()
.then(onFulfilled)
.catch(onRejected) // 等同 then(undefined, onRejected)
執行等待多個 promise 皆處理完畢 ( 靜態方法 all )
使用 all 執行陣列中的所有 promise 物件
Promise.all( [promise1, promise2 ....] ).then( onFulfilled ). catch( onReject )
- 所有 promise 都 resolve 後才會進入 onFulfilled
(onFulfilled 中傳入的 value 為陣列格式,保存各 promise 回傳的值) - 陣列順序與執行順序無關
- 所有 promise 其中任一發生錯誤或 reject 情況則馬上回傳
範例如下:
var promise1 = new Promise((resolve)=>{
setTimeout(()=>{
resolve('take 1 second')
},1000)
})
var promise2 = new Promise((resolve)=>{
setTimeout(()=>{
resolve('take 2 second')
},2000)
})
Promise.all([promise1, promise2])
.then((values)=>{
console.log(values) // ['take 1 second', 'take 2 second']
})
.catch((error)=>{
console.log(error)
})
建立已具狀態的 promise 物件 ( 靜態方法 resolve 與 reject )
直接產生 Fulfilled 已實現狀態的 promise 物件 ( 只會進入 onFulfilled )
Promise.resolve( value ).then(onFulfilled, onRejected)
直接產生 Rejected 已拒絕狀態的 promise 物件 ( 只會進入 onRejected )
Promise.reject( reason ).then(onFulfilled, onRejected)
// promise 成功要執行的方法
function onFulfilled (value) {
console.log(value)
}
// promise 失敗要執行的方法
function onRejected (reason) {
console.log(reason.message)
}
Promise.resolve('success').then(onFulfilled, onRejected)
// go onFulfilled and show "success"
Promise.reject(new Error('fail')).then(onFulfilled, onRejected)
// go onRejected and show "fail"