async/await 是 ES2017(ES8)的新特性,它是一种基于 Promise 实现的异步编程方式。async/await 也是一种语法糖。
1、async/await 实现了用同步方式来写异步代码(promise是链式调用形式写异步代码)
2、async 是“异步” 的简写,用来声明一个 function 是异步的
3、await 是 async wait 的简写,用于等待一个异步方法执行完成
4、await 只能出现在 async 函数中
在函数声明前添加 async 关键字,表示该函数是异步的。
async 函数的返回值是一个 Promise 对象。 async 函数返回的值可以通过 then() 方法回调函数的参数或者 await 表达式获取。
async 函数内部使用 return 语句返回的值会被自动封装成 Promise 对象,并且该对象的状态会根据 return 语句返回的值类型自动变为 resolved 或 rejected。
async function foo() {
return 'Hello World!'
}
foo().then(value => console.log(value))
// 'Hello World!'
async function bar() {
throw new Error('Something went wrong!')
}
bar().catch(error => console.error(error))
// 'Error: Something went wrong!'
如果 async 函数中没有 return 语句,则该函数返回一个 undefined 值的 Promise 对象。
async function foo() {
const result = await Promise.resolve('Hello World!')
console.log('Hello World!')
// 等同于 return Promise.resolve(undefined)
}
foo().then(value => console.log(value))
// 'Hello World!'
// undefined
拓展知识:
如果在async 函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。
Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例。
暂停和恢复执行: 在async 函数内部,可以使用 await 关键字来等待一个 Promise 对象的状态改变。当遇到 await 时,函数会暂停执行,并等待 Promise 对象的状态变为 resolved 或 rejected,然后恢复执行。
async function foo() {
console.log('开始执行')
await new Promise(resolve => setTimeout(resolve, 2000)) // 等待2秒钟
console.log('2秒钟后恢复执行')
return '完成'
}
console.log('调用函数')
foo().then(value => {
console.log('异步函数返回值:', value)
})
console.log('继续执行')
// 调用函数
// 开始执行
// 继续执行
// 2秒钟后恢复执行
// 异步函数返回值: 完成
在上面的示例中,我们定义了一个 foo 的 async 函数。首先在主线程中调用 foo 函数,然后输出开始执行和继续执行。接下来,我们使用 await 关键字等待一个 2 秒钟后 resolved 的 Promise 对象。在等待期间,函数会暂停执行,并且主线程可以继续执行其他代码。当 2 秒钟过去后,Promise 对象的状态变为 resolved,await 表达式会返回 resolved 的值(在此例中为 undefined),并且函数恢复执行,输出 2 秒钟后恢复执行。最后,foo 函数返回一个 resolved 状态的 Promise 对象,该对象的值为’完成’。在 then 方法的回调函数中,我们打印出异步函数的返回值完成。
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {}
// 对象的方法
let obj = { async foo() {} }
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars')
}
async getAvatar(name) {
const cache = await this.cachePromise
return cache.match(`/avatars/${name}.jpg`)
}
}
const storage = new Storage()
storage.getAvatar('jake').then(…)
// 箭头函数
const foo = async () => {}
await 只能在 async 函数内部使用,它会暂停 async 函数的执行,等待 Promise 对象的状态改变,然后再继续执行 async 函数。
正常情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。(promise 对象、async 函数、普通数据)
async function f() {
// 等同于 return 123
return await 123
}
f().then(v => console.log(v))
// 123
错误处理 与 try…catch 使用
await 命令后面的 Promise 对象如果变为 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收到。
async function f() {
await Promise.reject('出错了')
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了
任何一个 await 语句后面的 Promise 对象变为 reject 状态,那么整个 async 函数都会中断执行。
async function f() {
await Promise.reject('出错了')
await Promise.resolve('hello world') // 不会执行
}
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个 await 放在 try…catch 结构里面,这样不管这个异步操作是否成功,第二个 await 都会执行。
async function f() {
try {
await Promise.reject('出错了')
} catch(e) {
}
return await Promise.resolve('hello world')
}
f().then(v => console.log(v))
// hello world
另一种方法是 await 后面的 Promise 对象再跟一个 catch 方法,处理前面可能出现的错误。(了解)
async function f() {
await Promise.reject('出错了')
.catch(e => console.log(e))
return await Promise.resolve('hello world')
}
f().then(v => console.log(v))
// 出错了
// hello world
如果有多个 await 命令,可以统一放在 try…catch 结构中。
async function main() {
try {
const val1 = await firstStep()
const val2 = await secondStep(val1)
const val3 = await thirdStep(val1, val2)
console.log('Final: ', val3)
} catch (err) {
console.error(err)
}
}
async/await 是一种更加直观、简洁且易读的异步处理方式,适合处理复杂的异步操作流程和错误处理。而 promise 则更加底层,可以用于更细粒度的异步控制和组合操作。在实际开发中,可以根据具体的需求和场景选择合适的异步处理方式。
语法差异
promise 是基于回调函数的异步处理方式,使用 then 和 catch 方法来处理异步操作的结果和错误。
async/await 是一种基于生成器函数(Generator)和 promise 的语法糖,通过在函数前面加上 async 关键字,将函数转换为返回 promise 对象的异步函数,并使用 await 关键字等待异步操作的结果。
可读性
promise 以链式调用的形式书写异步代码。
async/await 以同步形式书写异步代码。更加直观和易读,代码结构更加清晰。
错误处理
promise 中,通常使用 catch 方法来捕获和处理错误。
async/await 中,通常使用 try…catch 语句来捕获和处理错误。
异步操作的顺序
promise 中,如果有多个异步操作需要按照特定的顺序执行,我们需要通过嵌套的 then 方法或使用 Promise.all、Promise.race 等组合方法来实现。
async/await 中,可以使用同步的代码风格来编写异步操作的顺序,使代码更加简洁和可读。
错误堆栈
promise 中,当发生错误时,错误信息中会包含详细的堆栈信息,可以追踪到错误发生的位置。
async/await 中,由于使用了 try…catch 语句捕获错误,堆栈信息可能会被截断,不够详细。
function fetchUser(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId === 1) {
resolve({ id: userId, name: 'Alice' })
} else {
reject(new Error('User not found'))
}
}, 1000)
})
}
// 使用 Promise 实现异步操作
fetchUser(1)
.then(user => console.log(user))
.catch(error => console.error(error))
// 使用 async/await 实现异步操作
async function getUser(userId) {
try {
const user = await fetchUser(userId)
console.log(user)
} catch (error) {
console.error(error)
}
}
getUser(1)