译者按:
在Async/Await替代Promise的6个理由中,我们比较了两种不同的异步编程方法:
Async/Await
和
Promise
,这篇博客将通过示例代码介绍
Async/Await
是如何简化JavaScript代码的。
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
Async/Await
是JavaScript的
ES7
新特性,来源于
.NET
和
C#
。它可以不用回调函数,像同步代码那些编写异步代码。这篇博客将通过一些代码示例,来说明
Async/Await
如何简化JavaScript代码。
1. 去除回调函数
运行本文的示例代码,并不需要额外的函数库。对于最新版的主流浏览器中,例如Chrome,Firefox, Safari以及Edge,它们都支持Async/Await语法。另外,Node.js 7.6+也支持了Async/Await语法。
我们编写了一些简单的API接口,用于模拟异步操作。这些接口都返回Promise,并在200ms后
resolve
一些数据。
class Api { constructor () { this.user = { id: 1, name: 'test' } this.friends = [ this.user, this.user, this.user ] this.photo = 'not a real photo' } getUser () { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.user), 200) }) } getFriends (userId) { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.friends.slice()), 200) }) } getPhoto (userId) { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.photo), 200) }) } throwError () { return new Promise((resolve, reject) => { setTimeout(() => reject(new Error('Intentional Error')), 200) }) } }
|
嵌套Promise
function
callbackHell () { const api = new Api() let user, friends api.getUser().then(function (returnedUser) { user = returnedUser api.getFriends(user.id).then(function (returnedFriends) { friends = returnedFriends api.getPhoto(user.id).then(function (photo) { console.log('callbackHell', { user, friends, photo }) }) }) }) }
|
曾经使用Promise编写回调函数的开发者一定不会陌生,这样一层层的嵌套代码通常是这样结尾的:
在回调函数中调用回调函数,一层层地嵌套,这就是所谓的“回调地狱”。在真实的代码中,这样的情况并不少见,通常更为复杂。
链式Promise
function promiseChain () { const api = new Api() let user, friends api.getUser() .then((returnedUser) => { user = returnedUser return api.getFriends(user.id) }) .then((returnedFriends) => { friends = returnedFriends return api.getPhoto(user.id) }) .then((photo) => { console.log('promiseChain', { user, friends, photo }) }) }
|
Promise的最佳特性之一,就是可以在
then
回调函数中,return一个新的Promise,这样就可以将这些Promise链接起来,只有一层嵌套。
链式Promise
比
嵌套Promise
简单很多,但是还是很多冗余。
Async/Await
不使用回调函数可以吗?当然可以!使用
Async/Await
的话,7行代码就可以搞定。
async function asyncAwaitIsYourNewBestFriend () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) const photo = await api.getPhoto(user.id) console.log('asyncAwaitIsYourNewBestFriend', { user, friends, photo }) }
|
使用
await
关键词时,赋值操作将等到异步操作结束时才进行。这样,看起来与同步代码无异,实际执行事实上是异步的。
2. 简化循环
Async/Await
可以让一些复杂操作,比如循环变得简单。例如,当我们需要获取某个user的所有friends的friends列表,应该怎样操作呢?
使用Promise
function promiseLoops () { const api = new Api() api.getUser() .then((user) => { return api.getFriends(user.id) }) .then((returnedFriends) => { const getFriendsOfFriends = (friends) => { if (friends.length > 0) { let friend = friends.pop() return api.getFriends(friend.id) .then((moreFriends) => { console.log('promiseLoops', moreFriends) return getFriendsOfFriends(friends) }) } } return getFriendsOfFriends(returnedFriends) }) }
|
我们使用了递归函数
getFriendsOfFriends
来获取
friends-of-friends
,知道
friends
数组为空。如此简单的任务,这样写显然过于复杂了。
使用
Promise.all()
来实现的话,则并非循环,而是并发执行。
使用Async/Await
This could be so much easier.
async function asyncAwaitLoops () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) for (let friend of friends) { let moreFriends = await api.getFriends(friend.id) console.log('asyncAwaitLoops', moreFriends) } }
|
这时,可以直接使用for循环来实现,非常简单。
3. 简化并发
使用循环逐个获取
friends-of-friends
显然太慢,采用并发方式更为简单。
async function asyncAwaitLoopsParallel () { const
api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) const friendPromises = friends.map(friend => api.getFriends(friend.id)) const moreFriends = await Promise.all(friendPromises) console.log('asyncAwaitLoopsParallel', moreFriends) }
|
为了实现并发,只需要将Promise数组作为
Promise.all()
的参数即可。这样,只需要
await
一个Promise,而这个Promise会在所有并发操作结束时
resolve
。
4. 简化错误处理
使用回调函数处理Promise错误
function callbackErrorHell () { const api = new Api() let user, friends api.getUser().then(function (returnedUser) { user = returnedUser api.getFriends(user.id).then(function (returnedFriends) { friends = returnedFriends api.throwError().then(function () { console.log('Error was not thrown') api.getPhoto(user.id).then(function (photo) { console.log('callbackErrorHell', { user, friends, photo }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }
|
这样做非常糟糕,代码非常冗余,可读性也很差。
使用catch方法处理Promise错误
function callbackErrorPromiseChain () { const api = new Api() let user, friends api.getUser() .then((returnedUser) => { user = returnedUser return api.getFriends(user.id) }) .then((returnedFriends) => { friends = returnedFriends return api.throwError() }) .then(() => { console.log('Error was not thrown') return api.getPhoto(user.id) }) .then((photo) => { console.log('callbackErrorPromiseChain', { user, friends, photo }) }) .catch((err) => { console.error(err) }) }
|
这样处理好多了,仅仅需要在Promise链的最后,使用catch方法处理所有错误。
使用Try/Catch处理Async/Await错误
async function aysncAwaitTryCatch () { try { const api = new
|