async 和 await
async 和 await 两种语法结合可以让异步代码像同步代码一样。(即:看起来是同步的,实质上是异步的。)
先从字面意思理解,async 意为异步,可以用于声明一个函数前,该函数是异步的。await 意为等待,即等待一个异步方法完成。
async
async 声明(function)的函数成为 async 函数,语法:
async function funcName() {
//statements
}Copy to clipboardErrorCopied
async 内部可以使用 await,也可以不使用。 async 函数的返回值是一个 Promise 对象,因此执行这个函数时,可以使用 then 和 catch 方法。 根据 函数体内部 的返回值, async 函数返回值具体情况如下:
-
函数体内不返回任何值,则
async函数返回值为一个成功(fulfilled)的Promise对象,状态值为undefined。let a = async function() {} let res = a() console.log(res) // Promise{<fullfilled>: undefined}Copy to clipboardErrorCopied -
返回结果不是一个
Promise,则async函数返回值为一个成功(fulfilled)的Promise对象,状态值为这个内部返回值。let a = async function () { return 'hello' } let res = a() console.log(res) // Promise{<fullfilled>: 'hello'}Copy to clipboardErrorCopied -
内部抛出错误,则
async函数返回值为一个失败的Promise对象。let a = async function foo() { throw new Error('出错了') } a().catch(reason => { console.log(reason) })Copy to clipboardErrorCopied -
若函数内部返回值是一个
Promise对象,则async函数返回值的状态取决于这个Promise对象。let a = async function () { return new Promise((resolve, reject) => { resolve("成功") }) } a().then(value => { console.log(value) })Copy to clipboardErrorCopied
await
await 相当于一个运算符,右边接一个值。一般为一个 Promise 对象,也可以是一个非 Promise 类型。当右接一个非 Promise 类型,await 表达式返回的值就是这个值;当右接一个 Promise 对象,则 await 表达式会阻塞后面的代码,等待当前 Promise 对象 resolve 的值。
综合 async 和 await 而言。await 必须结合 async 使用,而 async 则不一定需要 await。 async 会将其后的函数的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,然后返回 resolve 的结果。当这个 Promise 失败或者抛出异常时,需要时使用 try-catch 捕获处理。
Promise 使用链式调用解决了传统方式回调地狱的问题,而 async-await 又进一步优化了代码的可读性。
const p = new Promise((resolve, reject)=>{
resolve('成功')
})
async function main() {
let res = await p
console.log(res)
}
main()
// '成功'Copy to clipboardErrorCopied
const p = new Promise((resolve, reject)=>{
reject('失败')
})
async function main() {
try {
let res = await p
console.log(res)
} catch(e) {
console.log(e)
}
}
main()
// '失败'Copy to clipboardErrorCopied
综合应用-读取文件
需求:先读取用户数据 user,然后读取订单数据 order,最后读取商品数据 goods。
对于这种异步操作很容易想到使用 Promise,代码如下:
const fs = require('fs')
let p = new Promise((resolve, reject) => {
fs.readFile('./files/user.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
p.then(value => {
return new Promise((resolve, rejecet) => {
fs.readFile('./files/order.md', (err, data) => {
if (err) rejecet(err)
resolve([value, data])
})
})
}, reason => {
console.log(reason)
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./files/goods.md', (err, data) => {
if (err) reject(err)
value.push(data)
resolve(value)
})
})
}, reason => {
console.log(reason)
}).then(value => {
console.log(value.join('\n'))
}, reason => {
console.log(reason)
})Copy to clipboardErrorCopied
但是,使用 Promise 链式调用虽然避免了回调地狱,但这种链式调用过多难免引起代码复杂,看起来不直观。可以使用 async 和 await 方法优化,代码如下:
const fs = require('fs')
function readUser() {
return new Promise((resolve, reject) => {
fs.readFile('./files/user.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
function readOrder() {
return new Promise((resolve, reject) => {
fs.readFile('./files/order.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
function readGoods() {
return new Promise((resolve, reject) => {
fs.readFile('./files/goods.md', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
async function read() {
let user = await readUser()
let order = await readOrder()
let goods = await readGoods()
console.log([user, order, goods].join('\n'))
}
read()Copy to clipboardErrorCopied
这样,代码看起来很直观,就好像是同步代码一样,实际上是异步操作。
综合应用-封装ajax
function sendAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
}
reject(xhr.status)
}
}
})
}
async function main() {
let res = await sendAjax('http://poetry.apiopen.top/sentences')
let poem = res.result.name + '——' + res.result.from
document.body.innerText = poem
}
main()Copy to clipboardErrorCopied
这里封装的ajax还不能体现 async-await 的作用所在,因为没有出现多个 ajax 请求。在又多个 ajax 请求并且后续的请求依赖于前一个请求的结果的时候,async-await 的优点就体现出来了。