Promise
簡介
- 主要用於非同步操作,是一個由 asynchronous function return 的物件
- 用來表示一個異步操作的最终完成(或失敗)及其结果值
- 代理一個建立時不用預先得知結果的值
- 解決
callback hell
的問題......我的超人
語法
- 只能使用
new
關鍵字創建
new Promise(executor)
Promise 構造函式包含兩個參數:
resolve
- 將 promise 的狀態從 pending 更改為 fulfilled(成功)
- 將非同步函式執行後的結果作為參數傳送給
.then( )
reject
- 將 promise 的狀態從 pending 更改為 rejected (失敗)
- 將錯誤訊息作為參數傳送給
.catch( )
Promise 狀態
pending
:事件運行中,尚未取得結果(初始狀態)resolved
:事件已經執行完畢且成功操作,回傳 resolve 的結果(該承諾已經被實現 fulfilled)rejected
:事件已經執行完畢但操作失敗,回傳 rejected 的結果
promise()
.then((success) => {
console.log(success)
})
.catch((fail) => {
console.log(fail)
})
範例:創建一個 promise
const getUser = new Promise((resolve, reject) => {
// Do some async task
setTimeout(() => {
let error = false
if (!error) {
resolve({ name: 'Blur', age: 40 })
} else {
reject('Error: Something went wrong...')
}
}, 1000)
})
getUser
.then((user) => console.log(user))
.catch((error) => console.log(error))
.finally(() => console.log('The promise has been resolved or rejected'))
🤔 創建 Promise 構造函式,executor
內的程式碼是會立即執行的
範例 1
console.log('開始')
const myPromise = new Promise((resolve, reject) => {
console.log('Promise內部 (同步執行)')
resolve()
})
console.log('結束')
// output
// 開始
// Promise內部 (同步執行)
// 結束
範例 2
// This is a JavaScript Quiz from BFE.dev
console.log(1)
const promise = new Promise((resolve) => {
console.log(2)
resolve()
console.log(3)
})
console.log(4)
promise
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
console.log(7)
setTimeout(() => {
console.log(8)
}, 10)
setTimeout(() => {
console.log(9)
}, 0)
// 出現順序:1 2 3 4 7 5 6 9 8
🤔 如果有兩個resolve
時
- 一開始調用了
resolve(1)
,Promise 的狀態從Pending
被修改成fulfilled
- 後續的
resolve
和reject
都會被忽略
new Promise((resolve, reject) => {
resolve(1)
resolve(2)
reject('error')
}).then(
(value) => {
console.log(value)
},
(error) => {
console.log('error')
}
)
// output
// 1
範例:決定非同步函式的執行順序
- 希望執行順序: fun1 => fun2 => fun3
// 使用Promise
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 3000)
})
}
function fun2(data) {
return new Promise((resolve, rejcet) => {
setTimeout(() => {
resolve(data)
}, 2000)
})
}
function fun3(data) {
return new Promise((resolve, reject) => {
console.log(data)
})
}
fun1()
.then(fun2)
.then(fun3)
.catch((error) => {
console.log(error)
})
// 執行fun1,3秒後將resolve的結果'hello'作為參數回傳給fun2
// fun2接收到'hello',2秒後將resolve的結果'hello'作為參數回傳給fun3
// fun3 印出 'hello'
// 不使用Promsie
function fun1(callback) {
setTimeout(() => {
callback('hello')
}, 3000)
}
function fun2(data, callback) {
setTimeout(() => {
callback(data)
}, 2000)
}
function fun3(data) {
console.log(data)
}
//
fun1((resultFromFun1) => {
fun2(resultFromFun1, (resultFromFun2) => {
fun3(resultFromFun2)
})
})
範例:傳入的數字是否大於 5
//example
const getSomeData = (num) => {
return new Promise((resolve, reject) => {
if (num > 5) {
resolve('resolve number > 5')
}
reject('reject number < 5')
})
}
getSomeData(10)
.then((data) => {
return getSomeData(0)
})
.catch((err) => {
return getSomeData(10)
})
.then((data) => {
console.log(data)
})
.finally(() => {
console.log('finally')
})
Promise 方法
Promise.all
- 目的:讓多個 promise 同時執行
- 參數接受一個可迭代的物件,但通常會傳入 promise 組成的 array 作為參數
- 每個 promise 完成時,resolve 的值會存入陣列中
- 如果其中任何一個 Promise 失敗,會回傳第一個失敗的 Promise 物件
範例
//1
const fetchPromise1 = fetch('url1');
const fetchPromise2 = fetch('url2');
const fetchPromise3 = fetch('url3');
Promise.all([fetchPromise1,fetchPromise2,fetchPromise3])
//return 一個陣列
.then((responses) => {
responses.forEach((res)=> {
console.log(res)
})
})
)
//[result1, result2, result3]
//2
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3]).catch((error) => {
console.log(error); // "Error"
});
Promise.any
- 只要 Promise array 中的任何一個請求成功就執行.then(),並回傳這個 resolve 的結果
- 如果所有 promises 都被拒絕,則執行.catch(),回傳 AggregateError,其中包含所有 Promise 的拒絕原因
範例
const promises = [Promise.resolve('hello'), Promise.reject('failed'), Promise.resolve('world')]
Promise.any(promises)
.then((result) => console.log(result))
.catch((error) => console.error(error))
// hello
Promise.race
- 任何一個"陣列傳入參數的 Promise 物件有解決,就會到下一步去
範例
const getSomeData = (num, time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (num > 0) {
resolve(`${num} > 0`)
} else {
reject(`${num} < 0`)
}
}, time)
})
}
Promise.race([getSomeData(10, 500), getSomeData(5, 1000)])
.then((data) => {
console.log(data)
})
.catch((err) => {
console.log(err)
})
// '10 > 0'
async....await
- Promise 的語法糖,提高程式碼可讀性,少寫很多
.then()
- 所有的 async function 都會 return 一個 Promise Object
- async 宣告一個非同步函式,await 等待 Promise 的 resolve/reject
- await 會回傳 Promise resolve/reject 的結果
- 使用 try / catch 來區分 resolve/reject 要執行的區域
aysnc function myFunc() {
return 99;
}
let result = myFunc();
console.log(result) //得到一個Promise物件
//取得return的值
result.then((data) => console.log(data)); //99
範例:發出 get 請求
async function fetchProduct(){
try{
const response = await fetch(url');
const data = await response.json();
console.log(data);
} catch(err){
console.log(err)
}
}
fetchProduct();
範例:發出 post 請求
async function postData(url = '', data = {}) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
return response.json()
} catch (error) {
console.error(error)
throw error // 再次抛出異常,以便上層代碼進行進一步處理
}
}
// 使用範例:
postData('<https://example.com/api/posts>', { title: 'foo', body: 'bar', userId: 1 })
.then((data) => {
console.log(data)
})
.catch((error) => {
console.error(error)
})
範例:.then()
& await
寫法比較
//使用await
async function getApiData() {
const data = await fetch(url)
const result = await data.json()
console.log(result)
}
//使用.then
async function getApiData2() {
fetch(url)
.then((data) => data.json())
.then((result) => {
console.log(result)
})
}
範例:使用箭頭函式
const getApiData = async () => {
const response = await fetch(url)
.then((res) => {
return res.json()
})
.then((data) => console.log(data))
.catch((err) => console.log(err))
}
範例:try & catch 也可以使用在同步函式
function double(num) {
if (isNaN(num)) {
throw new Error(`${num} is not a number`)
}
return num * 2
}
try {
const x = double(3)
console.log(x) // 6
} catch (err) {
console.log(err)
}
參考資料
# JavaScript # ES6