了解在 JavaScript 中使用异步函数的现代方法。 JavaScript 在很短的时间内从回调函数演变为 Promises,而自从 ES2017 以来,使用 async/await 语法的异步 JavaScript 变得更加简单。
介绍
JavaScript 在很短的时间内从回调函数演变为 promises(ES2015),而自从 ES2017 以来,使用异步 JavaScript 的 async/await 语法变得更加简单。
异步函数是 promises 和 generators 的组合,基本上它们是对 promises 的更高级抽象。请记住:async/await 是建立在 promises 之上的。
为什么引入 async/await?
它们减少了 promises 的样板代码,并且消除了 promises 链式调用的“不中断链条”的限制。
在 ES2015 中引入 Promise 时,它们被用来解决异步代码的问题,并且确实解决了这个问题,但是在 ES2015 和 ES2017 之间的两年时间里,很明显 promises 不能成为最终解决方案。
Promises 被引入用来解决著名的“回调地狱”问题,但是它们自身引入了复杂性和语法复杂性。
它们是一个很好的原语,可以为开发人员提供更好的语法,所以在适当的时候,我们得到了 async 函数。
它们使代码看起来像是同步的,但在幕后是异步的和非阻塞的。
它是如何工作的
异步函数返回一个 promise,就像这个例子中的那样:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
当您想要调用此函数时,您在前面加上 await
,调用代码将等到 promise 被解析或拒绝之前停止。一个注意事项:客户端函数必须定义为async
。以下是一个示例:
const doSomething = async () => {
console.log(await doSomethingAsync())
}
一个简单的示例
这是一个简单的示例,它使用 async/await 异步运行一个函数:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
const doSomething = async () => {
console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
上面的代码将在浏览器控制台打印以下内容:
Before
After
I did something //after 3s
Promises 全覆盖
在任何函数前加上 async
关键字意味着该函数将返回一个 promise。
即使它没有明确地这样做,它也会在内部使其返回一个 promise。
这就是这段代码有效的原因:
const aFunction = async () => {
return 'test'
}
aFunction().then(alert) // 这会提示“test”
它与以下代码相同:
const aFunction = async () => {
return Promise.resolve('test')
}
aFunction().then(alert) // 这会提示“test”
代码更易读
正如您在上面的示例中看到的那样,我们的代码看起来非常简单。将其与使用普通 promises、链式调用和回调函数的代码进行比较。
当代码更加复杂时,将会有更大的优势。
例如,下面是使用 promises 获取并解析 JSON 资源的方式:
const getFirstUserData = () => {
return fetch('/users.json') // 获取用户列表
.then(response => response.json()) // 解析 JSON
.then(users => users[0]) // 选择第一个用户
.then(user => fetch(`/users/${user.name}`)) // 获取用户数据
.then(userResponse => userResponse.json()) // 解析 JSON
}
getFirstUserData()
以下是使用 await/async 提供相同功能的方式:
const getFirstUserData = async () => {
const response = await fetch('/users.json') // 获取用户列表
const users = await response.json() // 解析 JSON
const user = users[0] // 选择第一个用户
const userResponse = await fetch(`/users/${user.name}`) // 获取用户数据
const userData = await userResponse.json() // 解析 JSON
return userData
}
getFirstUserData()
串行多个异步函数
异步函数可以很容易地链接在一起,并且语法比使用普通 promises 更加可读:
const promiseToDoSomething = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 10000)
})
}
const watchOverSomeoneDoingSomething = async () => {
const something = await promiseToDoSomething()
return something + ' and I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
const something = await watchOverSomeoneDoingSomething()
return something + ' and I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then(res => {
console.log(res)
})
将打印:
I did something and I watched and I watched as well
更容易调试
调试 promises 很困难,因为调试器不会逐步执行异步代码。
Async/await 让这个过程变得非常容易因为对编译器来说,它就像是同步代码一样。