Skip to content

小小前端

明月本无心,行人自回首。

Menu
  • 前端开发
  • 编程技术
  • SQL语句
  • Linux
  • 生活/旅行
  • JSEditor
  • MiniBarCMS
  • About
  • 隐私政策
Menu

[转]Async/Await是这样简化JavaScript代码的

Posted on 2017年10月21日 by king2088

译者按: 在Async/Await替代Promise的6个理由中,我们比较了两种不同的异步编程方法:Async/Await和Promise,这篇博客将通过示例代码介绍Async/Await是如何简化JavaScript代码的。

原文: ASYNC/AWAIT WILL MAKE YOUR CODE SIMPLER
译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

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一些数据。

[cc lang=”javascript”]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)
})
}
}[/cc]

嵌套Promise

[cc lang=”javascript”]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 })
})
})
})
}[/cc]
曾经使用Promise编写回调函数的开发者一定不会陌生,这样一层层的嵌套代码通常是这样结尾的:

})
})
})
}
在回调函数中调用回调函数,一层层地嵌套,这就是所谓的“回调地狱”。在真实的代码中,这样的情况并不少见,通常更为复杂。

链式Promise

[cc lang=”javascript”]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 })
})
}[/cc]
Promise的最佳特性之一,就是可以在then回调函数中,return一个新的Promise,这样就可以将这些Promise链接起来,只有一层嵌套。链式Promise比嵌套Promise简单很多,但是还是很多冗余。

Async/Await

不使用回调函数可以吗?当然可以!使用Async/Await的话,7行代码就可以搞定。

[cc lang=”javascript”]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 })
}[/cc]
使用await关键词时,赋值操作将等到异步操作结束时才进行。这样,看起来与同步代码无异,实际执行事实上是异步的。

2. 简化循环

Async/Await可以让一些复杂操作,比如循环变得简单。例如,当我们需要获取某个user的所有friends的friends列表,应该怎样操作呢?

使用Promise

[cc lang=”javascript”]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)
})
}[/cc]
我们使用了递归函数getFriendsOfFriends来获取friends-of-friends,知道friends数组为空。如此简单的任务,这样写显然过于复杂了。

使用Promise.all()来实现的话,则并非循环,而是并发执行。

使用Async/Await

[cc lang=”javascript”]
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)
}
}[/cc]
这时,可以直接使用for循环来实现,非常简单。

3. 简化并发

使用循环逐个获取friends-of-friends显然太慢,采用并发方式更为简单。

[cc lang=”javascript”]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)
}[/cc]
为了实现并发,只需要将Promise数组作为Promise.all()的参数即可。这样,只需要await一个Promise,而这个Promise会在所有并发操作结束时resolve。

4. 简化错误处理

使用回调函数处理Promise错误

[cc lang=”javascript”]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)
})
}[/cc]
这样做非常糟糕,代码非常冗余,可读性也很差。

使用catch方法处理Promise错误

[cc lang=”javascript”]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)
})
}[/cc]
这样处理好多了,仅仅需要在Promise链的最后,使用catch方法处理所有错误。

使用Try/Catch处理Async/Await错误

[cc lang=”javascript”]async function aysncAwaitTryCatch () {
try {
const api = new Api()
const user = await api.getUser()
const friends = await api.getFriends(user.id)
await api.throwError()
console.log(‘Error was not thrown’)
const photo = await api.getPhoto(user.id)
console.log(‘async/await’, { user, friends, photo })
} catch (err) {
console.error(err)
}
}[/cc]
对于Async/Await代码,使用Try/Catch即可处理,和同步代码一样,更加简单。

如何你需要监控线上JavaScript代码的错误时,可以免费使用Fundebug的实时错误监控服务,只需要一行代码就可以搞定!

5. 简化代码组织

使用async关键词定义的函数都会返回Promise,这样可以更方便地组织代码。

例如,在之前的示例中,我们可以将获取的user信息return,而不是直接打印;然后,我们可以通过返回的Promise来获取user信息。

[cc lang=”javascript”]async function getUserInfo () {
const api = new Api()
const user = await api.getUser()
const friends = await api.getFriends(user.id)
const photo = await api.getPhoto(user.id)
return { user, friends, photo }
}
function promiseUserInfo () {
getUserInfo().then(({ user, friends, photo }) => {
console.log(‘promiseUserInfo’, { user, friends, photo })
})
}[/cc]
使用Async/Await语法,则更加简单:

[cc lang=”javascript”]async function awaitUserInfo () {
const { user, friends, photo } = await getUserInfo()
console.log(‘awaitUserInfo’, { user, friends, photo })
}[/cc]
如何获取多个user的信息?

[cc lang=”javascript”]async function getLotsOfUserData () {
const users = []
while (users.length < 10) { users.push(await getUserInfo()) } console.log('getLotsOfUserData', users) }[/cc] 如何并发?如何处理错误? [cc lang="javascript"]async function getLotsOfUserDataFaster () { try { const userPromises = Array(10).fill(getUserInfo()) const users = await Promise.all(userPromises) console.log('getLotsOfUserDataFaster', users) } catch (err) { console.error(err) } }[/cc] 本文转载地址:https://blog.fundebug.com/2017/10/16/async-await-simplify-javascript/

发表评论 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

分类

近期文章

  • cordova-plugin-camera在某些android机型中拍照或选择文件时闪退出错的解决办法 2019年10月24日
  • JavaScript nodeJS base64加密解密url参数 2019年10月15日
  • 利用expressJS编写reset api 2019年4月13日
  • angular4 + http拦截器 2019年3月21日
  • ionic navCtrl.pop如何传递参数给上一个页面 2018年11月16日
  • ionic3搭建开发/测试环境 2018年10月25日
  • ionic2、3双击硬件back按键退出应用 2018年10月24日
  • VMware安装Mac OS High Sierra 10.12及高版本无法全屏 2018年8月24日

近期评论

  • 手表资讯发表在《ReactJS环境搭建》
  • king2088发表在《ionic中使用热更新插件cordova-hot-code-push》
  • 重阳节的诗句发表在《常用的sql语句》
  • 新郎致辞发表在《PHP代码实现WordPress相关文章的几种方法》
  • 霸道总裁发表在《vsftpd 提示 unrecognized service 解决办法》

归档

标签

Ajax Android Angular APP Cordova CSS css3 express html5 ionic Java javascript jQuery Linux loading mac Mac OS mongodb MySQL node nodejs PHP react SQL SSH VirtualBox vue vue-cli win10 WordPress WP REST API 主题 兼容性 前端 备份 插件 数据库 数组 服务器 正则表达式 浏览器 热更新 目录 组件 错误
2023年 9月
一 二 三 四 五 六 日
 123
45678910
11121314151617
18192021222324
252627282930  
« 10月    
© 2023 小小前端 | Powered by Superbs Personal Blog theme