历史小剧场
懂得暴力的人,是强壮的;懂得克制暴力的人,才是强大的。----《明朝那些事儿》
什么是 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 p1fun
let p2value = await p2fun
console.log(p1value) // p1
console.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回调。