上一篇介紹到Promise可以用來處理非同步行為,但始終在閱讀性方面還是不是很優,今天要介紹的是基於Promise讓非同步的語法結構類似同步語言 --- Async/await,還有一些額外的優點也會一併介紹。
在一個函數前面加async可以確保此函數返回的是promise,其他返回的值都會被包裝成resolve:
async function test() {
return 10
}
test().then( res => console.log(res)) // 10
直接返回promise也是可以的:
async function test() {
return Promise.resolve(1)
}
test().then( res => console.log(res)) //1
首先await只能在async函數中運作,所以基本上async/await是一體的,不會單獨出現,await會讓JavaScript引擎等到promise完成(settle)且返回結果:
async function test() {
let result = await new Promise((resolve, reject) => {
setTimeout(() => resolve("成功!"),1000)
})
console.log(result);
}
test(); // 一秒後 "成功"
續用昨天文章中的函數來說明:
function ownPromise(count, time = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
count? resolve(`第${count}次成功!`) : reject("失敗")
},time)
})
}
我們可以得到以下結論:
console.log("Start")
async function fetchData() {
const dataA = await ownPromise(1);
const dataB = await ownPromise(2);
console.log(dataA, dataB)
}
fetchData();
console.log("End!")
//
Start!
End!
第1次成功! 第2次成功!
其中fetchData其實一樣是非同步的部分,但是透過await可以做到讓async function內的非同步是以同步的方式依序執行。
我們一樣用ownPromise來說明,promise可以透過chain的方式來做error handle,且一旦遇到錯誤會直接跳轉到catch:
ownPromise(0).then( res => {
console.log(res);
return ownPromise(1);
}).then( res => {
console.log(res);
return ownPromise(2);
}).catch( err => {
console.log(err);
})
// 失敗 ownPromise(0)失敗直接跳轉到catch
那async/await因為已經轉為同步的形式,所以如果遇到error沒有處理的話,後續的代碼都沒辦法運行,async/await的error handle一樣用try..catch來處理:
async function fetchData() {
try {
const dataA = await ownPromise(1);
const dataB = await ownPromise(0);
console.log(dataA, dataB)
} catch(err) {
console.log('catch error',err)
}
}
fetchData();
async/await因為讓非同步變成同步的形式,所以可以跟JavaScript語法一起搭配使用。
for..loop會等到當前的迴圈中的await有回應後,且執行完回圈所有代碼後才會進入下一個迴圈,所以非常適合用來依序發出request。
let arrayData = [
{count:1, time:1000},
{count:2, time:2000},
{count:3, time:3000},
{count:4, time:4000},
]
// 情況一
async function testForLoop() {
const resArray = [];
for(let i = 0; i < arrayData.length; i++) {
const item = arrayData[i]
resArray.push(ownPromise(item.count, item.time));
console.log(`${item.count}執行完畢`)
}
console.log(resArray)
}
testForLoop();
// 情況二
async function testForLoop() {
const resArray = [];
for(let i = 0; i < arrayData.length; i++) {
const item = arrayData[i]
resArray.push(await ownPromise(item.count, item.time));
console.log(`${item.count}執行完畢`)
}
console.log(resArray)
}
testForLoop();
forEach的概念是每個item依序執行callback function,但是一旦開始執行後就馬上接著換下一個item,所以會幾乎同時執行每個迴圈內容。
let arrayData = [
{count:1, time:1000},
{count:2, time:2000},
{count:3, time:3000},
{count:4, time:4000},
]
// 情況一
async function testForEach() {
const resArray = [];
arrayData.forEach(item => {
resArray.push(ownPromise(item.count, item.time))
console.log(`第${item.count}次執行完畢!`)
})
console.log(resArray);
for(const res of resArray) {
console.log(await res)
}
}
testForEach();
// 情況二
async function testForEach() {
const resArray = [];
arrayData.forEach(async item => {
resArray.push(await ownPromise(item.count, item.time))
console.log(`第${item.count}次執行完畢!`)
})
console.log(resArray);
}
testForEach();
與forEach類似,也是幾乎同時執行,但是會馬上執行return的動作,會無視async/await直接return。
let arrayData = [
{count:1, time:1000},
{count:2, time:2000},
{count:3, time:3000},
{count:4, time:4000},
]
// 情況一
async function testMap() {
let resArray = arrayData.map(item => {
return ownPromise(item.count, item.time)
})
console.log(resArray);
for(const res of resArray) {
console.log(await res)
}
}
testMap();
// 情況二
async function testMap() {
let resArray = arrayData.map(async item => {
return await ownPromise(item.count, item.time)
})
console.log(resArray);
for(const res of resArray) {
console.log(await res)
}
}
testMap();
所以情況一與二的結果是一樣的: