在前端編程中,經(jīng)常會遇到異步處理,今天小編為大家簡單講解JS中異步中很著名的async/await,結(jié)合之前講的Promise,相信大家讀完這篇文章后會對JS中的異步操作有更深的理解。
什么是異步操作?
在文章開始之前,先為不明白什么是異步操作的同學(xué)說一下什么是異步,簡單的講異步代碼的執(zhí)行,不會阻塞后面代碼的執(zhí)行。同學(xué)們可能會有疑問,JS作為一門單線程語言是如何做到異步的,這個(gè)知識點(diǎn)我會在后面的文章中為大家講解,這是因?yàn)镴S的Event Loop(事件循環(huán))。
本文章主要介紹async/await。那么什么是async/await呢?
async
首先async是作為一個(gè)關(guān)鍵字出現(xiàn)的,是ES6提出來的一個(gè)函數(shù),是針對Generator函數(shù)(也是ES6提出來的,后面的文章中為大家詳細(xì)講解Generator,閱讀本文章,暫時(shí)不需要深入了解)的一個(gè)語法糖,其用法很簡單,就是在普通函數(shù)前面加async,即代表這是一個(gè)異步函數(shù),代表他后面的代碼正常執(zhí)行,不會因?yàn)楫惒胶瘮?shù)的執(zhí)行,阻塞后面的函數(shù)。下面是一個(gè)簡單的async函數(shù)。
async function myFirstAsync () {
return "hello async"
}
myFirstAsync()
console.log("我是同步函數(shù)")
上面是一個(gè)簡單的async函數(shù),為了證明async函數(shù)不會阻塞后面代碼的執(zhí)行,我在后面又寫了一個(gè)同步方法。下面我們看一下控制臺執(zhí)行結(jié)果。
我們在控制臺看到了,async后面的函數(shù)可以正常執(zhí)行,這說明async不會阻塞后面函數(shù)的執(zhí)行。
async函數(shù)返回值是什么
感興趣的同學(xué)會疑問,async函數(shù)到底返回了什么,我們對上面代碼做簡單改造,如下
async function myFirstAsync () {
return "hello async"
}
console.log(myFirstAsync())
console.log("我是同步函數(shù)")
這時(shí)看控制臺
顯而易見,async返回的是一個(gè)Promise對象,同時(shí)從另一方面也證明了這是一個(gè)異步函數(shù)。既然是Promise我們想要拿到他的返回值,就得用到then,我們再次對上面的代碼進(jìn)行改造
async function myFirstAsync () {
return "hello async"
}
myFirstAsync().then((result) => {
console.log(result)
})
console.log("我是同步函數(shù)")
這時(shí)我們再看控制臺
這時(shí)我們拿到了async函數(shù)的返回值,細(xì)心的同學(xué)一定會發(fā)現(xiàn),“我是一個(gè)同步函數(shù)”是先于async函數(shù)打印的,而我們在通過
then取值之前是在async函數(shù)之后執(zhí)行的。其實(shí),到現(xiàn)在這一步,才是真正符合JS Event Loop的。上面只是我們看到了Promise,因?yàn)槲覀冎繮romise是異步的,到這里,我們從執(zhí)行順序上看到了async是一個(gè)異步函數(shù)。現(xiàn)在我們知道了async返回的是一個(gè)Promise,那么我們也一定能像Promise那樣捕獲他的“成功”和“失敗”的狀態(tài)。正如我們前面看到的那樣,“成功”的狀態(tài)會通過
resolve拋出,“失敗”的狀態(tài)會通過reject拋出,看下面代碼
async function myFirstAsync (flag) {
if(flag){
return "成功了!"
}else {
throw "失敗了!"
}
}
console.log(myFirstAsync(true))
console.log(myFirstAsync(false))
看控制臺輸出
當(dāng)然,我們也可以像Promise那樣,對錯(cuò)誤做統(tǒng)一處理,因?yàn)镻romise的錯(cuò)誤拋出是冒泡機(jī)制。這樣我們可以做集中的捕獲,我們對上面代碼做簡單修改如下
async function myFirstAsync (flag) {
if(flag){
return "成功了!"
}else {
throw "失敗了!"
}
}
myFirstAsync(true).then((result) => {
console.log(result)
})
myFirstAsync(false).catch((err) => {
console.log(err)
})
看控制臺
這是我們順利拿到了“成功”和“失敗”時(shí)的拋出值,就像Promise那樣,通過
.then拿到成功的值,通過.then或者.catch拿到失敗的值,那么async的所有操作,除了不用自己手動創(chuàng)建Promise對象以外,幾乎和Promise的操作一模一樣,并且我們知道,當(dāng)Promise邏輯復(fù)雜的時(shí)候,也會是出現(xiàn)許多的.then,類似地獄回調(diào)一樣,那么我們怎么像本文章標(biāo)題中寫的那樣,徹底告別地獄回調(diào)呢?
await
這才是async徹底告別地獄回調(diào)的關(guān)鍵,那么什么是await呢?await也是一個(gè)關(guān)鍵字,await英文翻譯為“等待”,那么它在等待什么呢?我想讀這篇文章進(jìn)入狀態(tài)的讀者已經(jīng)猜到了,它就是在等async成功。大多數(shù)情況下,await后面需要跟一個(gè)Promise表達(dá)式,并且一定需要注意的是await只能放在async里面!里面!里面!重要的是說三遍。
那么我們現(xiàn)在按照上面說的,寫一個(gè)完整的async/await方法。
function doublenum (num){
return new Promise ((resolve,reject) => {
setTimeout(
() => {
resolve(2 * num)
} ,2000)
})
}
async function getDoublenum(){
const result = await doublenum(5)
console.log(result )
}
getDoublenum()
這時(shí),我們會在2秒后在控制臺輸出10,因?yàn)楸容^簡單,這里不再截圖,這里我們的代碼看起來就像同步代碼一樣,不依賴任何結(jié)果。哪怕是邏輯再復(fù)雜,我們也可以像同步編程一樣寫異步操作。
現(xiàn)在我們讓我們的代碼進(jìn)行更多的數(shù)字操作
function doublenum (num){
return new Promise ((resolve,reject) => {
setTimeout(
() => {
resolve(2 * num)
} ,2000)
})
}
async function getDoublenum(){
const result1 = await doublenum(5)
const result2 = await doublenum(10)
const result3 = await doublenum(15)
const result4 = await doublenum(20)
const result5 = await doublenum(25)
const result6= await doublenum(30)
console.log(result1 + result2 + result3 +result4 +result5 +result6 )
console.log('我寫在后面,但是我先執(zhí)行')
}
getDoublenum()
這時(shí),我們看控制臺
控制臺會在12秒后打印210,這是我們意料之中的,意料之外的是,它并沒有像我們預(yù)期的那樣,讓同步代碼先執(zhí)行。這就是
await的作用,它阻塞了后面的同步代碼,這也正是async/await和Promise不同的地方,它讓異步代碼變成同步代碼一樣,所以JS Event Loop的微任務(wù)宏任務(wù)中并沒有有async/await。雖然他們是基于Promise的封裝。上面的代碼的執(zhí)行過程是這樣的,執(zhí)行到result1 ,等待兩秒,執(zhí)行result2,等待2秒,result2執(zhí)行完畢,再執(zhí)行result3,以此類推...所以說,我們的異步代碼,在用了await以后,是會停下來的,這也從原理上說明了為什么上面的代碼會阻塞同步代碼。結(jié)合全篇文章來看,使用
async/await,使我們的代碼變得更加簡潔,不會再出現(xiàn)地獄回調(diào),并且await的阻塞,為我們提供了一些特殊需求的解決方案。說到底,async/await和Promise還是很不同的,至于我們在日常的開發(fā)中使用哪種異步處理,就要因需求而定了,假如二者都可以滿足需求,這里建議使用async/await,至于二者的對比,這里不再贅述。網(wǎng)上有許多大牛的文章可供參考。文章到這里結(jié)束,謝謝您的瀏覽,有錯(cuò)誤之處聯(lián)系更正。