JavaScript 基础
Document
运算符
深入数据和类型
函数进阶
原型、继承
类
浏览器存储
Web API
事件
错误处理
异步编程
网络请求
模块
练习
实例
工具与规范
软件架构模式
设计模式
async、await
async 与 await 是两个关键字,能让我们用同步的写法来写异步的程序。
async
async
关键字用于函数前面,它能使用函数返回的的值变成一个 promise:
$$jsdemo$$
$$edit$$
async function func() {
return "三眼鸭"
}
let promise = func()
console.log(promise)
它与以下的代码是等价的:
$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
resolve("三眼鸭")
})
console.log(promise)
单单是这样并不能体现 async 的作用, async 需要搭配 await 使用才能起到效果。
await
await
关键字表示等待 promise
执行完成并得到执行结果, await
只能在 async
函数中使用。
假设我们有一个使用 promise
语法的实例如下。
$$jsdemo$$
$$edit$$
const promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("三眼鸭")
}, 3000)
})
promise.then(function (result) {
console.log(result) // 三眼鸭,等待执行完成
console.log("执行完毕") // 执行完毕
})
使用 async、await
的语法改写如下。
$$jsdemo$$
$$edit$$
const promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("三眼鸭")
}, 3000)
})
async function func() {
const result = await promise
console.log(result) // 三眼鸭,等待执行完成
console.log("执行完毕") // 执行完毕
}
func()
await
并不意味着会阻塞 JavaScript 的主线程的执行, 它阻塞的只是 async
函数的执行。
$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("三眼鸭")
}, 3000)
})
async function func() {
const result = await promise
console.log(result) // 三眼鸭,等待执行完成
console.log("执行完毕") // 执行完毕
}
func()
console.log("END") // 最先执行
async、await 的好处
async
与 await
最大的好处就是让我们用同步的思维去写异步的程序。
假设我们有以下代码,要下载一个文件,这个文件的下载链接包含在 A 文件当中,而 A 文件的下载链接包含在 B 中, B 又包含在 C 中。
那么我们要依次下载 C → B → A → 最终文件,那么代码就得写成以下这样:
$$jsdemo$$
$$edit$$
function download() {
// 这个下载花费 2 秒钟
return new Promise((resolve, reject) => {
let file = "我是文件哦"
setTimeout(() => resolve(file), 2000)
})
}
console.log("开始执行了~")
console.log("先去下个文件~")
// 下载好了通知我就行,不等你了
download().then((file) => {
console.log("下载 C 完成")
console.log("拿到 B 的链接")
download().then((file) => {
console.log("下载 B 完成")
console.log("拿到 A 的链接")
download().then((file) => {
console.log("下载 A 完成")
console.log("拿到 最终文件 的链接")
download().then((file) => {
console.log("下载 最终文件 完成")
})
})
})
})
console.log("做其他事情去了~")
我们会陷入回调地狱中,回调越写越多,代码执行顺序越来越混乱。
当然,由于 then
中的返回值会作为下一个 then
的参数,因此我们可以改写如下。
$$jsdemo$$
$$edit$$
function download() {
// 这个下载花费 2 秒钟
return new Promise((resolve, reject) => {
let file = "我是文件哦"
setTimeout(() => resolve(file), 2000)
})
}
console.log("开始执行了~")
console.log("先去下个文件~")
// 下载好了通知我就行,不等你了
download()
.then((file) => {
console.log("下载 C 完成")
console.log("拿到 B 的链接")
return download()
})
.then((file) => {
console.log("下载 B 完成")
console.log("拿到 A 的链接")
return download()
})
.then((file) => {
console.log("下载 A 完成")
console.log("拿到 最终文件 的链接")
return download()
})
.then((file) => {
console.log("下载 最终文件 完成")
return download()
})
console.log("做其他事情去了~")
但依旧是不够简洁,那么就可以使用 asnyc
、 await
改写我们的程序了:
$$jsdemo$$
$$edit$$
function download() {
// 这个下载花费 2 秒钟
return new Promise((resolve, reject) => {
let file = "我是文件哦"
setTimeout(() => resolve(file), 5000)
})
}
async function getFile() {
let file = await download()
console.log("下载 C 完成")
console.log("拿到 B 的链接")
file = await download()
console.log("下载 B 完成")
console.log("拿到 A 的链接")
file = await download()
console.log("下载 A 完成")
console.log("拿到 最终文件 的链接")
file = await download()
console.log("下载 A 完成")
console.log("下载 最终文件 完成")
}
console.log("开始执行了~")
console.log("先去下个文件~")
getFile()
console.log("做其他事情去了~")
$$tip
async、await
使得我们可以用同步的写法来写异步的代码,特别在于执行的结果是下一个执行的必需参数的时候。
$$
错误处理
await
返回的是 resolve
的结果,如果是 reject
则会抛出一个错误,我们需要处理这个错误的话则需要使用 try ... catch
。
$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("三眼鸭"))
}, 3000)
})
async function func() {
try {
let result = await promise // throw new Error("三眼鸭")
alert(result)
alert("执行完毕")
} catch (error) {
alert(error) // Error: 三眼鸭
}
}
func()
同理,我们在 async
函数中 throw
一个错误时就等同于 promise
中的 reject
。
$$jsdemo$$
$$edit$$
async function func() {
// 等同于 promise 中的
// reject("一个错误")
throw "一个错误"
}
func().catch((error) => alert(error)) // 一个错误
练习
- 将
func
的代码改成使用async、await
的语法执行。
$$jsdemo$$
$$edit$$
function doAfter3s() {
return new Promise(function (resolve) {
setTimeout(function () {
resolve("三眼鸭的编程教室")
}, 3000)
})
}
function run() {
// 改成使用 async、await 的方式执行
doAfter3s().then((word) => alert(word))
}
func()
$$answer
$$jsdemo$$
$$edit$$
function doAfter3s() {
return new Promise(function (resolve) {
setTimeout(function () {
resolve("三眼鸭的编程教室")
}, 3000)
})
}
async function run() {
const word = await doAfter3s()
alert(word)
}
run()
$$
- 以下代码的打印顺序是什么,为什么?
function B() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("B")
resolve()
}, 1000)
})
}
function D() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("D")
resolve()
}, 1000)
})
}
async function run() {
console.log("A")
await B()
console.log("C")
D()
console.log("E")
}
run()
console.log("F")
$$answer
$$jsdemo$$
$$edit$$
function B() {
return new Promise(function (resolve) {
setTimeout(function () {
// 3. 距离 B() 一秒后执行
console.log("B")
resolve()
}, 1000)
})
}
function D() {
return new Promise(function (resolve) {
setTimeout(function () {
// 6. 距离 D() 一秒后执行
console.log("D")
resolve()
}, 1000)
})
}
async function run() {
// 1. 执行了 run() 首先执行这里
console.log("A")
// await 等同于把之后的所有代码放到 then 中
// 因此这里只相当于执行了 B()
// then 中的代码等到 B() resolve 时才执行
B().then(function () {
// 4. 此时 B() resolve 了
console.log("C")
// 仅是调用 D() 的 promise
D()
// 5. D() 没有 await、所以直接执行这里
console.log("E")
})
}
run()
// 2. run() 执行完成后执行
console.log("F")
$$