除了Promise
之外,還有async/await
語法來處理非同步程式,它背後的操作原理與Promise
是一樣的,所以也被稱為Promise
的語法糖,它可以把Promise
寫得更直覺和簡潔(但其實也是見人見智)。
這篇文篇會整理以下知識:
async
函式、await
Promise
與async/await
寫法async/await
抓遠端資料async
要寫在function
前面:
async function func() {
return 10;
}
func() //Promise {<fulfilled>: 10}
async
函式一定會回傳一個Promise
物件,即使例子中的func
不是一個Promise
物件,它也會被包裝成Promise
物件。
await
一定要寫在async
函式裏,否則會報錯。所以寫await
時,就要一併寫async
。
await
工作還未完成,就不會跑後面的程式碼在async
函式裏,一定會等await
的工作完成,才繼續跑該await
後面的程式。這樣的寫法使程式碼看起來更像同步執行(同步即是「做完一件事才接著做下一件事」)。
第一個例子:
async function func(){
const p = await new Promise( (resolve,reject) =>
window.setTimeout( () => resolve(100),1000) )
console.log('跑完p才跑我') //跑完p才跑我
console.log(p) //100
}
func()
以上例子可見,在async
函裏const p = ...
之後的程式碼,需要等const p = ...
跑完才會被執行。
第二個例子:
用setTimeout
延遲計算,完成以下的事(要做完1才可做2)。
//把num1和num2相加
function add(num1,num2){
return new Promise( (resolve, reject) => {
window.setTimeout( () => {
resolve( console.log(num1 + num2) );
},1000)
})
}
//以下看起來像同步執行的感覺
async function func(){
//p1跑完,才跑p2
const p1 = await add(10,20); //30
const p2 = await add(30,40); //70
}
func()
當我們要執行多次非同步程式時,async/await的寫法會更易閱讀。以下例子是先把數字1放到func
裏乘以10,之後把每次回傳結果乘以10。我們用此例子來模擬執行多次非同步程式時的情況。
如果用async/await
去寫:
function func(num){
return new Promise( (resolve, reject) => {
resolve(num * 10)
})
}
async function results(){
const result1 = await func(1);
console.log(result1); //10
const result2 = await func(result1);
console.log(result2); //100
const result3 = await func(result2);
console.log(result3); //1000
}
results()
如果用Promise
去寫:
function func(num){
return new Promise( (resolve, reject) => {
resolve( num * 10)
})
}
func(1)
.then( success => {
console.log(success); //10
return(func(success));
})
.then( success => {
console.log(success); //100
return(func(success));
})
.then( success => {
console.log(success); //1000
})
比較兩種寫法,async/await
的寫法裏,因為用了await
,讓人一看就知道這裏會處理一個非同步程式,而且要等它完成才會跑下面的程式。所以async/await
的寫法會更易閱讀。
catch
處理錯誤情況如果前一個await
出錯,那麼後面的程式就不會跑了:
function add(num1,num2){
return new Promise( (resolve, reject) => {
window.setTimeout( () => {
reject( num1 + num2 ); //在這裏改成reject,模擬出錯
},1000)
})
}
async function func(){
const p1 = await add(10,20);
const p2 = await add(30,40);
return `第一次相加結果:${p1},第二次相加結果:${p2}` //Uncaught (in promise) 30
}
func()
這時候async
函式會報錯:Uncaught (in promise) 30
。
剛才提及過,async
本身會回傳Promise
物件,所以我們可以像平時一樣,用then
和catch
方法來抽取Promise
物件裏的失敗/成功值。這裏我們用catch
來處理錯誤情況:
function add(num1,num2){
return new Promise( (resolve, reject) => {
window.setTimeout( () => {
reject( num1 + num2 ); //在這裏改成reject,模擬出錯
},1000)
})
}
async function func(){
const p1 = await add(10,20);
const p2 = await add(30,40);
return `第一次相加結果:${p1},第二次相加結果:${p2}`
}
func()
.then( success => {
console.log(`成功!`,success)
})
.catch( error => {
console.log(`錯誤!`,`最後相加結果:${error}`) //錯誤! 最後相加結果:30
})
try...catch
處理錯誤情況除了then
和catch
這個組合,另一個方法是用try
和catch
的語法去處理。直接在async
函式裏寫就可以了:
function add(num1,num2){
return new Promise( (resolve, reject) => {
window.setTimeout( () => {
reject( num1 + num2 ); //在這裏改成reject,模擬出錯
},1000)
})
}
async function func(){
try{
const p1 = await add(10,20);
const p2 = await add(30,40);
console.log(`第一次相加結果:${p1},第二次相加結果:${p2}`)
} catch(error){
console.log(`錯誤!`,`最後相加結果:${error}`) //錯誤! 最後相加結果:30
}
}
func()
try...catch
用法題外話,try...catch
是一個針對錯誤處理的語法,原理跟Promise
裏的then
和catch
一樣,只要出現錯誤,就會跳去跑catch
裏的程式。catch(...)
裏的err
的名稱是可以自訂的。當try
裏面的程式出現錯誤,JavaScript會把錯誤原因放到catch(...)
裏的参數中,即是err
。
try {
// code...
} catch (err) { // err是try區塊裏程式出錯的原因
// error handling
}
另外,catch
後面的参數可以不寫,這樣就不會回傳錯誤原因給你。例如以下範例(参考自這裏),因為在JSON.parse(JSONdata)
出錯,所以會執行catch
裏的程式:
let JSONdata = {'錯誤JSON格式': 123123};
try{
//JSONdata的格式不合乎JSON的格式,所以JSON.parse會出錯
let data = JSON.parse(JSONdata);
console.log(data);
}catch{
console.log('JSON資料格式錯誤!') //'JSON資料格式錯誤!'
}
async/await
抓遠端資料最常處理非同步程式就是抓遠端資料的情況了。以下例子用async/await
和XMLHttpRequest
來抓遠端資料,並且用try...catch
來處理成功或錯誤結果:
let getJSON = url => {
return new Promise( (resolve,reject) => {
let xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(null);
xhr.onload = () => {
if(xhr.status === 200){
resolve(JSON.parse(xhr.responseText));
}else{
reject(new Error(xhr.statusText));
}
}
})
}
//直接在async函式裏,用try...catch處理成功或失敗的結果
async function printJSON(url){
try{
const data = await getJSON(url);
console.log(data); //顯示回傳資料 {results: Array(1), info: {…}}
}catch(error){
console.log('錯誤!',error);
}
}
printJSON('https://randomuser.me/api/')
async/await
是Promise
的語法糖,背後運作原理與Promise
是一樣,作用是把Promise
寫得更易讀async
要寫在function
前面async
函式一定會回傳一個Promise
物件await
要放在async
函式裏面async/await
的錯誤處理方法:
catch
方法(就如平時寫Promise
一樣)try...catch
方法JS 原力覺醒 Day16 - Async / Await:Promise 語法糖
JAVASCRIPT.INFO - Async/await
鐵人賽:JavaScript Await 與 Async
告別 JavaScript 的 Promise!迎接 Async/Await 的到來
JAVASCRIPT.INFO - Error handling, "try..catch"