async/await是JavaScript為了解決非同步問題而提出的一種解決方案,許多人將其稱為非同步的終極解決方案。JavaScript的發展也經歷了回撥、Promise、async/await三個階段,本篇文章記錄了我自己對於async/await的理解。因為async/await的使用離不開Promise,如果對於Promise不熟悉的話,可以看下這篇介紹:前端萌新眼中的Promise及使用。
一、async/await的具體使用規則
在我們處理非同步的時候,比起回撥函式,Promise的then方法會顯得較為簡潔和清晰,但是在處理多個彼此之間相互依賴的請求的時候,就會顯的有些累贅。這時候,用async和await更加優雅,後面會詳情說明。
async/await使用規則一:凡是在前面新增了async的函式在執行後都會自動返回一個Promise物件
例子:
async function test() {
}
let result = test()
console.log(result) //即便程式碼裡test函式什麼都沒返回,我們依然打出了Promise物件
複製程式碼
async/await使用規則二:await必須在async函式裡使用,不能單獨使用
錯誤的例子:
function test() {
let result = await Promise.resolve('success')
console.log(result)
}
test() //執行以後會報錯
複製程式碼
正確的例子:
async test() {
let result = await Promise.resolve('success')
console.log(result)
}
test()
複製程式碼
async/await使用規則三:await後面需要跟Promise物件,不然就沒有意義,而且await後面的Promise物件不必寫then,因為await的作用之一就是獲取後面Promise物件成功狀態傳遞出來的引數。
正確的例子:
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
})
})
}
async test() {
let result = await fn() //因為fn會返回一個Promise物件
console.log(result) //這裡會打出Promise成功後傳遞過來的'success'
}
test()
複製程式碼
沒有意義的例子(不會報錯):
async test() {
let result = await 123
console.log(result)
}
test()
複製程式碼
二、async/await的錯誤處理方式
關於錯誤處理,如規則三所說,await可以直接獲取到後面Promise成功狀態傳遞的引數,但是卻捕捉不到失敗狀態。在這裡,我們通過給包裹await的async函式新增then/catch方法來解決,因為根據規則一,async函式本身就會返回一個Promise物件。
一個包含錯誤處理的完整的async/await例子:
let promiseDemo = new Promise((resolve, reject) => {
setTimeout(() => {
let random = Math.random()
if (random >= 0.5) {
resolve('success')
} else {
reject('failed')
}
}, 1000)
})
async function test() {
let result = await promiseDemo
return result //這裡的result是promiseDemo成功狀態的值,如果失敗了,程式碼就直接跳到下面的catch了
}
test().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
複製程式碼
上面的程式碼需要注意兩個地方,一是async函式需要主動return一下,如果Promise的狀態是成功的,那麼return的這個值就會被下面的then方法捕捉到;二是,如果async函式有任何錯誤,都被catch捕捉到!
三、同步與非同步
在async函式中使用await,那麼await這裡的程式碼就會變成同步的了,意思就是說只有等await後面的Promise執行完成得到結果才會繼續下去,await就是等待,這樣雖然避免了非同步,但是它也會阻塞程式碼,所以使用的時候要考慮周全。
比如下面的程式碼:
function fn(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${name}成功`)
}, 1000)
})
}
async function test() {
let p1 = await fn('小紅')
let p2 = await fn('小明')
let p3 = await fn('小華')
return [p1, p2, p3]
}
test().then(result => {
console.log(result)
}).catch(result => {
console.log(result)
})
複製程式碼
這樣寫雖然是可以的,但是這裡await會阻塞程式碼,每個await都必須等後面的fn()執行完成才會執行下一行程式碼,所以test函式執行需要3秒。如果不是遇到特定的場景,最好還是不要這樣用。
三、一個小測試
寫到這裡,突然想起Promise的程式碼執行順序也是挺需要注意的。
請看下面的程式碼,執行完以後打出的數字的順序是怎樣的呢?
console.log(1)
let promiseDemo = new Promise((resolve, reject) => {
console.log(2)
setTimeout(() => {
let random = Math.random()
if (random >= 0.2) {
resolve('success')
console.log(3)
} else {
reject('failed')
console.log(3)
}
}, 1000)
})
async function test() {
console.log(4)
let result = await promiseDemo
return result
}
test().then(result => {
console.log(5)
}).catch((result) => {
console.log(5)
})
console.log(6)
複製程式碼
答案是:1 2 4 6 3 5
四、一個適合使用async/await的業務場景
在前端程式設計中,我們偶爾會遇到這樣一個場景:我們需要傳送多個請求,而後面請求的傳送總是需要依賴上一個請求返回的資料。對於這個問題,我們既可以用的Promise的鏈式呼叫來解決,也可以用async/await來解決,然而後者會更簡潔些。
使用Promise鏈式呼叫來處理:
function request(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time)
}, time)
})
}
request(500).then(result => {
return request(result + 1000)
}).then(result => {
return request(result + 1000)
}).then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
複製程式碼
使用async/await的來處理:
function request(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time)
}, time)
})
}
async function getResult() {
let p1 = await request(500)
let p2 = await request(p1 + 1000)
let p3 = await request(p2 + 1000)
return p3
}
getResult().then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
複製程式碼
相對於使用then不停地進行鏈式呼叫, 使用async/await會顯的更加易讀一些。
五、在迴圈中使用await
如果在是迴圈中使用await,就需要牢記一條:必須在async函式中使用。
在for...of
中使用await:
let request = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(time)
}, time)
})
}
let times = [1000, 500, 2000]
async function test() {
let result = []
for (let item of times) {
let temp = await request(item)
result.push(temp)
}
return result
}
test().then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
複製程式碼
上面就是我今天關於async/await理解的記錄,筆者作為一隻程式設計師生活只有半年,以上內容估計還有錯誤之處,如果掘金上的朋友有看到,還望不吝指出!謝謝您的觀看!