了解在 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 让这个过程变得非常容易因为对编译器来说,它就像是同步代码一样。