ES6: 用 Promise 處理非同步問題的解題技巧
透過 Promise 處理非同步操作,可確保事件完成後再執行。我們會在 Promise 建構函式中使用 resolve 或 reject 來控制執行流程。當執行非同步事件時,Promise 會維持 pending 狀態,直到事件完成並呼叫 resolve 成為 fulfilled 或因錯誤而呼叫 reject 成為 rejected。使用 then、catch、finally 來處理 Promise 的結果,以確保非同步操作的正確性和時序。
ES6 新增了一個新語法:Promise。當我們在 JavaScript 使用 fetch() 函式傳入一段 url,它會 return 一個 Promise,這個 Promise 是一個獨特的物件。
當非同步發生時,如果我們想要確保執行的順序是在完成非同步事件後,才往下執行的話就可以用 Promise 處理。(過去會用 Callback, ES7 則可以用語法糖 Async Await)
一、Promise 建構函式
Promise 這個函式必須傳入一個參數 (該參數為函式),這參數函式又包含兩個參數(都為函式),分別為 resolve 和 reject
而 resolve
和 reject
,這兩個函式分別代表成功與失敗的回傳結果,且這兩個方法是 JaveScript 引擎幫我們準備好了,無需再額外定義,但我們要注意的地方為兩個函式只能回傳一個,當回傳結果後,一個 Promise 就代表結束了。
function Promise((resolve, reject) => {
resolve() // 正確完成的回傳方法
reject() // 失敗的回傳方法
})
二、Promise 物件: 以 Promise 建構函式為原型
Promise 物件以 Promise 建構函式為原型,並使用 new Promise() 建立的物件
const p = new Promise()
三、Promise 物件的狀態
Promise 本身有三種狀態: pending、fulfilled、rejected
Promise 的狀態一開始會是 pending , 一旦 resovle() 被使用,狀態就會轉變為 fulfilled,而 reject() 被使用,狀態就會被轉變為 rejected。
- pending (擱置)
- 事件已經運行中,尚未取得結果
- fulfilled (完成、實現)
- 事件已經執行完畢且成功操作,回傳 resolve 的結果
- 意思是該承諾 Promise 已經被實現 fulfilled
- rejected (拒絕)
- 事件已經執行完畢但操作失敗,回傳 rejected 的結果
let cookFoodPromise = (foodName, time) => {
return new Promise((resolve, reject) => {
if (time > 3 && 5 > time) {
resolve(`${foodName}完美`)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
四、Promise 物件的方法
Promise 物件一旦建立起來就有 then、catch、finally 等方法可以呼叫
let cookFoodPromise = (foodName, time) => {
return new Promise((resolve, reject) => {
if (time > 3 && 5 > time) {
resolve(`${foodName}完美`)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
const cookTime = parseInt(Math.random() * 10) // 隨機帶入分鐘
cookFoodPromise('泡麵', cookTime)
.then((res) => { console.log(res) }) // "泡麵完美"
.catch((err) => { console.log(err) }) // "泡麵失敗"
.finally(() => { console.log('done')}) // "done"
// 若是沒執行 resolve() 也沒執行 reject()
console.log(cookFoodPromise('泡麵', cookTime)) // Promise {<pending>}
使用 then 來串接上一個的結果繼續做事情
let cookFoodPromise = (foodName, time) => {
return new Promise((resolve, reject) => {
if (time > 3 && 5 > time) {
resolve(`${foodName}完美`)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
const cookTime = parseInt(Math.random() * 10) // 隨機帶入分鐘
cookFoodPromise('泡麵', cookTime)
.then((res) => { return res + '好吃'} )
.then((res) => { console.log(res)}) // "泡麵完美好吃"
.catch((err) => { console.log(err) }) // "泡麵失敗"
.finally(() => { console.log('done')}) // "done"
五、Promise 物件進階方法: Promise.all()
Promise.all() 可以同時執行大量 Promise 物件,並且在全部完成後回傳陣列
一旦有 Promise 物件失敗,將回傳失敗那個物件回傳的結果,如果是全部失敗,則回傳第一個 Promise 物件的失敗結果,來當成整個最後的錯誤訊息。
這個方法很適合用在多支 API 要一起執行,並確保全部完成後才進行其他工作時。
1. 成功
let cookFoodPromise = (foodName, cookTime, timer) => {
return new Promise((resolve, reject) => {
if (cookTime > 3 && 6 > cookTime) {
setTimeout(() => {
resolve(`${foodName}完美`)
}, timer)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
let arr = [cookFoodPromise('花雕雞泡麵', 5, 1500), cookFoodPromise('麻油雞泡麵', 5, 3500)]
Promise.all(arrCookFoodPromise)
.then(res => console.log(res)) // ["花雕雞泡麵完美", "麻油雞泡麵完美"]
2. 一次失敗
let cookFoodPromise = (foodName, cookTime, timer) => {
return new Promise((resolve, reject) => {
if (cookTime > 3 && 6 > cookTime) {
setTimeout(() => {
resolve(`${foodName}完美`)
}, timer)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
let arr = [cookFoodPromise('花雕雞泡麵', 5, 2500), cookFoodPromise('麻油雞泡麵', 2, 1500)]
Promise.all(arr)
.then(res => console.log(res))
.catch(err => console.log(err)) // "麻油雞泡麵失敗"
3. 全部失敗
let cookFoodPromise = (foodName, cookTime, timer) => {
return new Promise((resolve, reject) => {
if (cookTime > 3 && 6 > cookTime) {
setTimeout(() => {
resolve(`${foodName}完美`)
}, timer)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
let arr = [cookFoodPromise('花雕雞泡麵', 2, 2500), cookFoodPromise('麻油雞泡麵', 2, 1500)]
Promise.all(arr)
.catch(err => console.log(err)) // "花雕雞泡麵失敗"
六、Promise 物件進階方法: Promise.race()
Promise.race() 只要有一個 Promise 物件回傳結果,不論成功或失敗都會結束該次 呼叫
透過陣列的形式傳入多個 promise 函式,在全部執行完成後回傳單一結果,結果為第一個運行完成的。(回傳最快回應的結果)
1. 成功
let cookFoodPromise = (foodName, cookTime, timer) => {
return new Promise((resolve, reject) => {
if (cookTime > 3 && 6 > cookTime) {
setTimeout(() => {
resolve(`${foodName}完美`)
}, timer)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
let arr = [cookFoodPromise('花雕雞泡麵', 5, 4500), cookFoodPromise('麻油雞泡麵', 5, 3500)]
Promise.race(arr)
.then(res => console.log(res)) // "麻油雞泡麵完美"
.catch(err => console.log(err))
2. 失敗
let cookFoodPromise = (foodName, cookTime, timer) => {
return new Promise((resolve, reject) => {
if (cookTime > 3 && 6 > cookTime) {
setTimeout(() => {
resolve(`${foodName}完美`)
}, timer)
} else if (time <= 3) {
reject((`${foodName}失敗`))
}
})
}
let arr = [cookFoodPromise('花雕雞泡麵', 2, 1500), cookFoodPromise('麻油雞泡麵', 5, 3500)]
Promise.race(arr)
.then(res => console.log(res))
.catch(err => console.log(err)) // "花雕雞泡麵失敗"
參考資料
1. 你今天 Promise 了嗎?
2. JavaScript Promise 全介紹
3. Promise 的使用
4. JavaScript - Promise (2)