進階 JavaScript 語法
try catch、Promise 和 async await
try catch
try catch 可以處理程式的錯誤,讓程式不會因為錯誤而影響執行
也可以利用這種寫法進行流程控制
避免出現錯誤訊息
使用一個不存在的函式
const message = () => {
try {
// 使用不存在的函式,觸發錯誤
alertt("Welcome guest!");
} catch(err) {
// 處理錯誤
console.log(err.message);
}
}
message();流程控制
輸入驗證流程範例
const validate = (input) => {
try {
// 可以使用 throw 拋出錯誤,通常會使用 new Error
if(input == '') throw new Error('empty');
if(isNaN(input)) throw new Error('not a number');
if(input < 5) throw new Error('too low');
if(input > 10) throw new Error('too high');
console.log('驗證成功');
}
catch(err) {
console.log(`驗證失敗: ${err.message}`);
}
}
validate(1);練習
製作註冊資料驗證 function,使用 throw 拋出錯誤,並用 try catch 處理
驗證規則
- 帳號不能空白
- 帳號長度需為 4 ~ 20 個字
- 密碼至少需要 8 個字
- 密碼不能和帳號相同
const register = (account, password) => {
// ... 在此寫你的程式碼
}
console.log(register(prompt('帳號'), prompt('密碼')))測試資料
| 帳號 | 密碼 | 輸出 |
|---|---|---|
| (空白) | 12345678 | 驗證失敗: 帳號不能空白 |
| abc | 12345678 | 驗證失敗: 帳號長度需為 4 ~ 20 個字 |
| abcd | 1234 | 驗證失敗: 密碼至少需要 8 個字 |
| abcd1234 | abcd1234 | 驗證失敗: 密碼不能和帳號相同 |
| abcd | 12345678 | 註冊成功 |
Promise
Promise 就跟單字的意思一樣,就是承諾
當承諾達成時,執行 .then() 內的程式,若有錯誤則執行 .catch() 內的程式
假設媽媽說,如果你這禮拜考試及格,下個禮拜就給你零用錢
現在你不知道到底會不會拿到零用錢,有可能你考試順利及格,也有可能因為沒讀書而考不好
這就是一個 Promise ,且有三種狀態
pending還沒發生或等待中,你還不知道考試結果resolved順利通過考試,拿到零用錢rejected沒讀書考不好,沒有零用錢
以上比喻轉成 Promise 物件程式碼
const passed = true
const willGetMoney = new Promise((resolve, reject) => {
if (passed) {
resolve('得到零用錢')
} else {
reject(new Error('考試不及格'))
}
})
willGetMoney.then(res => {
console.log(res)
}).catch(err => {
console.log(err.message)
})也可以包成 function
const willGetMoney = passed => {
return new Promise((resolve, reject) => {
if (passed) {
resolve('得到零用錢')
} else {
reject(new Error('考試不及格'))
}
})
}
willGetMoney(true).then(res => {
console.log(res)
}).catch(err => {
console.log(err.message)
})練習
製作抽獎程式
const draw = () => {
return new Promise((resolve, reject) => {
// ... 在此寫你的程式碼
})
}- 點抽獎按鈕後,等待 2 秒才公布結果
- 有 30% 的機率中獎,中獎時出現
恭喜中獎、沒中獎時出現錯誤沒有中獎
async await
將 function 加上 async,就可以在 function 內使用等待程式執行完畢的 await,變成同步函式
也可以透過 await 的幫助,改善 Promise 可讀性

注意
await 只能加在 async function 內,且 await 只能加在 Promise 函式
const wait = time => {
return new Promise((resolve, reject) => {
// 只能接受數字
if(isNaN(time)) {
reject(new Error('不是數字'))
}
// 以 setTimeout 模擬長時間運算
setTimeout(() => {
resolve(`過了 ${time / 1000} 秒`)
}, time)
})
}
const asyncfunc = async () => {
// 建議搭配 try catch 使用
try {
// 當 await 的 promise 成功時,可以直接用變數接 .then() 內的值
const msg1 = await wait(3000)
console.log('msg1' + msg1)
const msg2 = await wait(2000)
console.log('msg2' + msg2)
// 當 await 的 promise 有錯誤時,會導向 下面的 catch
// const msg3 = await wait('abc')
// console.log('msg3' + msg3)
console.log('finish')
return msg1 + msg2
} catch (e) {
console.log('error' + e.message)
return Promise.reject(new Error(e.message))
}
}
// async function 回傳的資料型態是 Promise,所以要用 .then() 接
// 也可以用 Promise.reject 拋出錯誤,讓 .catch() 接
asyncfunc().then(result => {
console.log(result)
}).catch(err => {
console.log(err)
})注意
在使用 await 的同時,不要錯過同時執行的機會
執行這段程式碼需要 3000 毫秒
const func1 = async () => {
const wait1 = await wait(1500);
const wait2 = await wait(1500);
return wait1 + wait2;
}
func1()但是執行這段程式碼只需要 1500 毫秒,因為兩個 wait 同時發生
const func2 = async () => {
let wait1 = wait(1500);
let wait2 = wait(1500);
wait1 = await wait1;
wait2 = await wait2;
return wait1 + wait2;
}
func2()練習
將上一題的抽獎程式,改用 async await 和 try catch 寫法顯示結果
Event Loop
JavaScript 一次只能做一件事,稱為單執行緒
等待 setTimeout 或 AJAX 時網頁不會卡住,是因為有 Event Loop 在安排程式的執行順序
- 一般程式碼會放在
Call Stack依序執行 - 遇到
setTimeout、AJAX 這類需要等待的工作,會交給瀏覽器處理,不會卡在Call Stack - 等待完成後,要執行的 function 會放進佇列排隊
Event Loop會不斷檢查,等Call Stack空了,才從佇列拿出下一個 function 執行
佇列又分成兩種,Promise 的 .then() 會放在優先度較高的 Microtask Queue
所以會比 setTimeout 先執行
console.log('1')
setTimeout(() => console.log('2'), 0)
Promise.resolve().then(() => console.log('3'))
console.log('4')
// 輸出順序: 1 4 3 2注意
如果迴圈一直執行不結束,Call Stack 永遠不會空,其他程式和事件都沒機會執行,網頁就會卡死await 會暫停 async function 並讓出執行權,其他程式才有機會執行
延伸閱讀
- JavaScript Visualizer 9000 視覺化工具,可以貼上程式碼觀察執行順序
- What the heck is the event loop anyway? JSConf EU 2014 演講影片
- MDN:並行模型和事件循環
綜合練習
練習
製作紅綠燈
網頁上有一個燈號,使用 async await 依序切換
- 紅燈亮 3 秒
- 綠燈亮 2 秒
- 黃燈亮 1 秒
提示: 可以使用本章的 wait() function