歷史小劇場
懂得暴力的人,是強壯的;懂得克制暴力的人,才是強大的。----《明朝那些事兒》
什么是 async/await
- async: 聲明一個異步函數
- 自動將常規函數轉換成Promise,返回值也是一個Promise對象;
- 只有async函數內部的異步操作執行完,才會執行then方法指定的回調函數;
- 異步函數內部可以使用await
- await: 暫停異步的功能執行
- 放置在Promise調用之前,await強制其他代碼等待,直到Promise完成并返回結果;
- 只能與Promise一起使用,不適用回調;
- 只能在async函數內部使用
簡單使用
async function fun() {// let name = await "后盾人"// console.log(name) let name = await new Promise(resolve => {setTimeout(() => {resolve("后盾人")}, 1000)})let site = await new Promise(resolve => {setTimeout(() => {resolve("www.houdunren.com")}, 2000)})console.log("name => ", name)console.log("site => ", site)}// console.log(fun()) // Promise {<fulfilled>: undefined}// fun().then(value => console.log(value))fun()
聲明方式
- 普通函數
async function get(dictType) {const dict = await ajax(`http://xxx?dict=${dictType}`)console.log("dict => ", dict)
}
- 函數表達式
const foo = async function (dictType) {const dict = await ajax(`http://xxx?dict=${dictType}`)console.log("dict => ", dict)
}
- 箭頭函數
const foo = async () => {const dict = await ajax(`http://xxx?dict=${dictType}`)console.log("dict => ", dict)
}
- 對象
let obj = {async get() {const dict = await ajax(`http://xxx?dict=${dictType}`)console.log("dict => ", dict)}
}
- class 類
class Dict {constructor(dictType) {this.dictType = dictType}async get() {let dictList = await ajax(`http://xxx?dict=${this.dictType}`)dictList.data.forEach(dictItem => {dictItem.AREA_NAME += '-心潮'})return dictList.data;}
}
new Dict('DICT_DRUG_AREA').get().then(value => {console.log("value => ", value)
})
錯誤處理
- async錯誤處理
async function get() {console.log(a)
}
get().catch(error => {console.log("error => ", error) // error => ReferenceError: a is not defined
})async function get2() {throw new Error("用戶不存在")
}
get2().catch(error => {console.log("error2 => ", error) // error2 => Error: 用戶不存在
})
- await 錯誤處理
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script src="../js/ajax.js"></script><script>// 1、在函數外部處理// async function get(dictType) {// let dictList = await ajax(`ahttp://xxx?dict=${dictType}`)// return dictList;// }// get('DICT_DRUG_AREA').then(res => {// console.log("res => ", res.data)// }).catch(error => {// console.log("error => ", error.message)// })// 2、在函數內部處理async function get(dictType) {try {let dictList = await ajax(`ahttp://xxx?dict=${dictType}`)return dictList;} catch (error) {alert(error.message)}}get('DICT_DRUG_AREA').then(res => {console.log("res => ", res.data)})</script>
</body>
</html>
ajax.js文件
class ParamError extends Error {constructor(msg) {super(msg)this.name = 'ParamError'}
}class HttpError extends Error {constructor(msg) {super(msg)this.name = 'HttpError'}
}function ajax(url) {// 自定義錯誤處理if (!/^http/.test(url)) {throw new ParamError("請求地址格式錯誤")}return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("POST", url)xhr.send()xhr.onload = function () {if (this.status === 200) {resolve(JSON.parse(this.response))} else if (this.status === 404) {// 自定義錯誤處理reject(new HttpError("響應內容不存在"))} else {reject("加載失敗")}}xhr.onerror = function () {reject(this)}})
}
場景案例
1. async延時函數
const sleep = async (delay = 2000) => {return new Promise(resolve => {setTimeout(() => {resolve()}, delay)})
}const show = async () => {for (name of ['小芳', '小紅']) {await sleep()console.log(name)}
}
show()
2. await 進度條
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {margin: 0;padding: 0;}#loading {width: 0vw;background-color: blueviolet;height: 10vh;display: flex;color: white;align-items: center;justify-content: center;transition: all 1s ease-out;font-size: 20px;}</style>
</head>
<body><div id="loading">0%</div><script src="../js/ajax.js"></script><script>(async (dicts) => {const loading = document.querySelector("#loading");for (let i = 0, len = dicts.length; i < len; i++) {await ajax(`http://xxx?dict=${dicts[i]}`)loading.style.width = `${(i + 1) / len * 100}vw`loading.textContent = `${Math.round((i + 1) / len * 100)}%`}})(Array(50).fill('DICT_DRUG_AREA'))</script>
</body>
</html>
3. await 并行技巧
首先,這里,有兩個異步任務
function p1() {return new Promise(resolve => {setTimeout(() => {resolve("p1")}, 2000)})
}function p2() {return new Promise(resolve => {setTimeout(() => {resolve("p2")}, 2000)})
}
當我們正常使用時,會順序隔兩秒打印p1和p2,代碼如下:
// 按順序打印
async function fun() {let p1value = await p1()console.log(p1value)let p2value = await p2()console.log(p2value)
}
fun()
顯然,這不是我們想要的效果。接著,我們這樣修改
fun()// 過兩秒之后 并行打印
async function fun() {let p1value = p1()let p2value = p2()setTimeout(() => {console.log(p1value) // Promise { 'p1' }console.log(p2value) // Promise { 'p2' }}, 2000)
}
這里,我們沒有使用await,那么返回的是Promise對象。
如上代碼,運行之后,會隔兩秒鐘之后同時打印 Promise { ‘p1’ } 和 Promise { ‘p2’ }
這樣,我們就找到了并行打印技巧
方案一
async function fun() {let p1fun = p1()let p2fun = p2()let p1value = await p1funlet p2value = await p2funconsole.log(p1value) // p1console.log(p2value) // p2
}
方案二 (推薦)
async function fun2() {const res = await Promise.all([p1(), p2()])console.log("res => ", res) // res => [ 'p1', 'p2' ]
}
fun2()
小結
Async/Await讓我們用少量的代碼來使用Promise,我們可以將一些有依賴關系的回調函數的處理邏輯放在async里面,然后在非async的區域使用,這樣可以減少then或者catch回調。