快速導覽 :
- Promise 例子
- Promise 狀態
- Promise 建立
- Promise 回傳資料與串接
- Promise 常用資料回傳方法
- 透過 Promise改寫事件佇列
Promise
Promise 是 ES6所新增的建構函式,常用來處理同步與非同步的問題,也增強了程式碼的可讀性。而 JS的執行順序是由上到下,若遇到事件則為進入事件佇列( Event queue )中,就是非同步的問題,像是 :
- AJAX行為
- 監聽事件(click、change、…)
setTimeout( () => { } )
Promise 與 AJAX
我們可以用原生 fetch語法或是直接使用第三方套件的 Axios來做 AJAX行為。
這邊提供一個資料測試網址(點我),它可以取得一些 JSON格式的假資料。
axios
以下是 axios套件寫法 :
1 2 3 4 5 6 7 8 9
|
var url = 'https://randomuser.me/api'
axios.get(url).then(res=>{ console.log(res) }).catch(err =>{ console.log(err) })
|
fetch
利用 fetch原生語法來進行 :
1 2 3 4 5 6 7 8 9 10 11 12
| var url = 'https://randomuser.me/api'
fetch(url) .then(response => { return response.json(); }) .then(jsonData => { console.log(jsonData); })
|
XMLHttpRequest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var url = 'https://randomuser.me/api'
function get(url){ return new Promise((resolve, reject)=>{ var req = new XMLHttpRequest() req.open('GET', url) req.onload = function (){ if(req.status = 200){ resolve(req.response) console.log(req.response) } else { } } req.send() }) } get(url).then((res)=>{ console.log('get', res) }).catch((err)=>{ console.log(err) })
|
Promise 與事件佇列
通常我們會使用一些監聽事件等待使用者去觸發,或是用一個計時器,例如這樣 :
一般事件佇列
1 2 3 4 5 6 7 8 9 10
| function fn(){ setTimeout( () => { console.log('等待出現') }, 0) alert('使用者點擊') }
var btn = document.querySelector('.btn') btn.addEventListener('click', fn)
|
但如果目標是讓每經過一秒顯示一次呢?
1 2 3 4 5 6 7 8 9
|
setTimeout( () => { console.log('等待出現') }, 1000) setTimeout( () => { console.log('等待出現') }, 1000)
|
透過 Promise來操作事件佇列
像是上述的例子,可以透過 Promise來處理問題。
以下透過 Promise來操作事件佇列 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function activeFn(time){ return new Promise((resolve, reject)=>{ setTimeout( () => { resolve(`${time}毫秒後自動顯示`) }, time) }) }
activeFn(1000).then(res=>{ console.log(res) return activeFn(1000) }).then(res=>{ console.log(res) })
|
Promise 狀態
Promise會有三種狀態,分別是 :
狀態名稱 |
意義 |
Pending |
待機 / 未確認,以 AJAX行為來說,就是傳入網址等待取回資料 |
Fulfilled |
已實現狀態,以 AJAX行為來說,就是取值成功時 |
Rejected |
已否決狀態,以 AJAX行為來說,就是取值失敗時 |
以上一次只會有一種狀態,並在實際使用時分別用不同的關鍵字取用。
- 若進入 fulfilled
- 使用 then接收上一個 return的值
- 使用函式將值作為參數傳入
.then((參數名稱)=>{console.log(參數)})
- 若進入 rejected
- 使用 catch接收錯誤時會回報的值
- 使用函式將值作為參數傳入
.catch((參數名稱)=>{console.log(參數)})
Promise 建立
- Promise是一個內建的函式
(typeof Promise) // "function"
- 可以使用 new運算子轉為物件,但它需要傳入一個 function才能運作
- 根據使用者認為該
- 情況正確使用
resoleve()
回傳資料
- 若不正確是用
reject()
回傳失敗訊息
- resolve與 reject可自定義參數名稱,不過為了避免混淆,還是習慣使用預設名稱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function promiseFn(num){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ if(num){ resolve('成功!') } else { reject('失敗!') } }, 10) }) } promiseFn(1).then((res)=>{ console.log(res) }).catch((res)=>{ console.log(res) })
|
Promise 回傳資料與串接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| function promiseFn(num){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ if(num){ resolve('成功!') } else { reject('失敗!') } }, 10) }) } promiseFn(1).then((res)=>{ console.log(res) return promiseFn(2) }).then((res)=>{ console.log(res) }).catch((res)=>{ console.log(res) return promiseFn(4) }).then((res)=>{ console.log(res) })
promiseFn(1).then( (res)=>{ console.log(res, 'success') return promiseFn(0) }, (rej)=>{ console.log(rej, 'fail') return promiseFn(1) }).then( (res)=>{ console.log('成功!') }, (rej)=>{ console.log('失敗!') })
|
Promise 常用資料回傳方法
- Promise.all
- 等待全部執行完畢,並回傳所有結果
- 途中只要有狀態為 reject,會直接回傳 catch結果
- Promise.race
- 只會回傳第一個執行完畢的函式並根據狀態回傳結果
- 只會回傳一組資料
Promise.all
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function promiseFn(num, time){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ if(num){ resolve('成功!') } else { reject('失敗!') } }, time) }) } promiseFn(1, 2000).then((res)=>{ console.log(res) })
Promise.all([ promiseFn(1, 1000), promiseFn(0, 2000), promiseFn(1, 3000) ]).then(res=>{ console.log(res[0], res[1], res[2]) })
|
Promise.race
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function promiseFn(num, time){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ if(num){ resolve('成功!') } else { reject('失敗!') } }, time) }) } promiseFn(1, 2000).then((res)=>{ console.log(res) }).catch((res)=>{ console.log(res) })
Promise.race([ promiseFn(1, 1000), promiseFn(1, 500), promiseFn(1, 3000) ]).then(res=>{ console.log(res) }).catch((res)=>{ console.log(res) })
|
事件佇列與一般結果
若想在事件佇列執行完畢後顯示其他結果,該怎麼做?
1 2 3 4 5 6 7 8 9 10 11 12
| function activeFn(time){ setTimeout( () => { console.log(`${time}毫秒後自動顯示`) }, time) } function activeFn2(){ console.log('最後顯示') } activeFn(3000) activeFn2()
|
利用 Promise修正結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function activeFn(time){ return new Promise((resolve, reject)=>{ setTimeout( () => { resolve(`${time}毫秒後自動顯示`) }, time) }) } function activeFn2(){ return new Promise((resolve, reject)=>{ resolve(`最後顯示`) }) }
activeFn(3000).then(res=>{ console.log(res) return activeFn2() }).then(res=>{ console.log(res) })
|
參考來源
- 六角學院 - 核心篇
- OXXO.STUDIO - JavaScript 同步延遲 ( Promise + setTimeout )