JavaScript中異步從來都不簡單,很長一段時間內, 我們都是使用的回調來實現。後來,我們可以使用promise
,現在,我們可以使用async
和await
(以下簡稱異步函數)來實現異步。
雖然異步函數的出現使得編寫異步函數更加容易了,但是同樣也存在陷阱,並且對於初學者並不是那麼的友好。
在這篇文章的兩個部分中,我將和你們一起分享一切你需要知道的關於異步函數的知識。
Asynchronous functions
異步函數包含async
關鍵字,你可以像普通的函數聲明一樣使用它:
async function functionName (arguments) {
//Do something asynchronous
}
你也可以使用箭頭函數
const functionName = async (arguments) => {
//Do something asynchronous
}
Asynchronous functions always return promise
返回什麼值並不重要,使用異步函數返回的值總是一個promise
const getOne = async _ => {
return 1;
}
const promise = getOne();
console.log(promise) // Promise
await關鍵詞
當你調用一個promise
時,你使用then
來進行下一步,就像這樣:
const getOne = async _ => {
return 1;
}
getOne()
.then(value => {
console.log(value); // 1
})
await
關鍵詞讓你定義reslove
函數,一旦promise
成功了,它就會返回傳遞到then
函數的參數。
const test = async _ => {
const one = await getOne();
console.log(one);
}
test();
Return await
在返回一個promise
之前,等待是沒有必要的,你可以直接返回promise
.
如果你返回一個await
, 你先實現了原來的promise,然後,你又創建一個新的promise
,return await
雖然不影響什麼,但是沒有必要多此一舉。
//Don't need to do this
const test = async _ => {
return await getOne();
}
test()
.then(value => {
console.log(value); // 1
})
//Do this instead
const test = aysnc _ => {
return getOne();
}
test()
.then(value => {
console.log(value); //1
})
注意,如果你不需要await
,你不許要使用異步函數。上面的列子可重寫。
//Do this instead
const test = _ => {
return getOne();
}
test()
.then(value => {
console.log(value); // 1
});
Handling errors
如果一個promise
導致了錯誤,你可以使用catch
來捕捉他
const getOne = aysnc (success = true) => {
if (success) return 1;
throw new Error('failure');
}
getOne(false)
.then(error => console.log(error)); // failure
如果你想在異步函數中處理錯誤,你需要使用一個try/catch
來捕捉它。
const test = async _ => {
try {
const one = await getOne(false);
}catch (e) {
console.log(e); //failure
}
}
test();
如果你有多個await
關鍵詞,這樣寫錯誤處理會變的很醜
const test = async _ => {
try {
const one = await getOne(false);
}catch (e) {
console.log(e); //failure
}
try {
const two = await getTwo(false);
}catch (e) {
console.log(e); //failure
}
try {
const three = await getThree(false);
}catch (e) {
console.log(e); //failure
}
}
test();
但是還有一個更好的辦法。
我們知道異步函數總是返回一個promise
,當我們調用一個promise
,我們在catch
中處理錯誤。這意味這我們可以通過添加catch
來處理錯誤。
const test = async _ => {
const one = await getOne(false);
const two = await getTwo(false);
const three = await getThree(false);
}
test()
.catch(error => console.log(error));
注意: Promise
方法只會讓你捕捉一個錯誤。
Multiple awaits
await
會阻塞js執行下一行代碼,知道promise
的resolve
被執行完成。這可能會降低執行的效率。
我們需要創建一個延遲來在實際中演示,通過sleep
來創建延遲。
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
}
//Using Sleep
console.log('Now');
sleep(1000)
.then(value => console.log('after one second')); // print 'now' immediately, 'after one second' printed in console after 1 second
現在我們來看需要等三個promise
的情況,每一個promise
都有一個一秒的延遲。
const getOne = _ => {
return sleep(1000).then(val => 1)
}
const getTwo = _ => {
return sleep(1000).then(val => 1)
}
const getThree = _ => {
return sleep(1000).then(val => 1)
}
如果你await
這些promises在一行中,你將不得不等待3秒在那些promise全部結束之前。這不是很好因爲我們強迫JavaScript等待額外的2秒在做我們需要的之前。
const test = async _ => {
const one = await getOne();
console.log(one);
const two = await getTwo();
console.log(two);
const three = await getThree();
console.log(three);
console.log('Done');
}
test(); //Console shows ‘Now’ immediately. One second later, it shows 1. Another second later, it shows 2. Another second later, it shows 3 and ‘Done’ at the same time.
如果getOne
, getTwo
, getThree
可以同時(simultraneously)執行,你可以節省兩秒。你可以使用promise.all
同時執行三個promise
.
const test = async _ => {
const promise = [getOne(), getTwo(), getThree()];
console.log('Now');
const [one, two, three] = await Promise.all(promise);
console.log(one);
console.log(two);
console.log(three);
console.log('Done');
};
Console shows ‘Now’ immediately. After one second, console shows 1, 2, 3, and ‘Done’
歡迎補充。