在 Node.js 中使用 async 函数的方法


Posted in Javascript onNovember 17, 2017

借助于新版 V8 引擎,Node.js 从 7.6 开始支持 async 函数特性。今年 10 月 31 日,Node.js 8 也开始成为新的长期支持版本,因此你完全可以放心大胆地在你的代码中使用 async 函数了。在这边文章里,我会简要地介绍一下什么是 async 函数,以及它会如何改变我们编写 Node.js 应用的方式。

1 什么是 async 函数

利用 async 函数,你可以把基于 Promise 的异步代码写得就像同步代码一样。一旦你使用 async 关键字来定义了一个函数,那你就可以在这个函数内使用 await 关键字。当一个 async 函数被调用时,它会返回一个 Promise。当这个 async 函数返回一个值时,那个 Promise 就会被实现;而如果函数中抛出一个错误,那么 Promise 就会被拒绝。

await 关键字可以被用来等待一个 Promise 被解决并返回其实现的值。如果传给 await 的值不是一个 Promise,那它会把这个值转化为一个已解决的 Promise。

const rp = require('request-promise')
async function main () {
 const result = await rp('https://google.com')
 const twenty = await 20
 // 睡个1秒钟
 await new Promise (resolve => {
  setTimeout(resolve, 1000)
 })
 return result
}
main()
 .then(console.log)
 .catch(console.error)

2 向 async 函数迁移

如果你的 Node.js 应用已经在使用Promise,那你只需要把原先的链式调用改写为对你的这些 Promise 进行 await。

如果你的应用还在使用回调函数,那你应该以渐进的方式转向使用 async 函数。你可以在开发一些新功能的时候使用这项新技术。当你必须调用一些旧有的代码时,你可以简单地把它们包裹成为 Promise 再用新的方式调用。

要做到这一点,你可以使用内建的 util.promisify方法:

const util = require('util')
const {readFile} = require('fs')
const readFileAsync = util.promisify(readFile)
async function main () {
 const result = await readFileAsync('.gitignore')
 return result
}
main()
 .then(console.log)
 .catch(console.error)

3 Async 函数的最佳实践

3.1 在 express 中使用 async 函数

express 本来就支持 Promise,所以在 express 中使用 async 函数是比较简单的:

const express = require('express')
const app = express()
app.get('/', async (request, response) => {
 // 在这里等待 Promise
 // 如果你只是在等待一个单独的 Promise,你其实可以直接将将它作为返回值返回,不需要使用 await 去等待。
 const result = await getContent()
 response.send(result)
})
app.listen(process.env.PORT)

但正如 Keith Smith 所指出的,上面这个例子有一个严重的问题——如果 Promise 最终被拒绝,由于这里没有进行错误处理,那这个 express 路由处理器就会被挂起。

为了修正这个问题,你应该把你的异步处理器包裹在一个对错误进行处理的函数中:

const awaitHandlerFactory = (middleware) => {
 return async (req, res, next) => {
  try {
   await middleware(req, res, next)
  } catch (err) {
   next(err)
  }
 }
}
// 然后这样使用:
app.get('/', awaitHandlerFactory(async (request, response) => {
 const result = await getContent()
 response.send(result)
}))

3.2 并行执行

比如说你正在编写这样一个程序,一个操作需要两个输入,其中一个来自于数据库,另一个则来自于一个外部服务:

async function main () {
 const user = await Users.fetch(userId)
 const product = await Products.fetch(productId)
 await makePurchase(user, product)
}

在这个例子中,会发生什么呢?

你的代码会首先去获取 user,

然后获取 product,

最后再进行支付。

如你所见,由于前两步之间并没有相互依赖关系,其实你完全可以将它们并行执行。这里,你应该使用 Promise.all 方法:

async function main () {
 const [user, product] = await Promise.all([
  Users.fetch(userId),
  Products.fetch(productId)
 ])
 await makePurchase(user, product)
}

而有时候,你只需要其中最快被解决的 Promise 的返回值——这时,你可以使用 Promise.race 方法。

3.3 错误处理

考虑下面这个例子:

async function main () {
 await new Promise((resolve, reject) => {
  reject(new Error('error'))
 })
}
main()
 .then(console.log)

当执行这段代码的时候,你会看到类似这样的信息:

(node:69738) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: error

(node:69738) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

在较新的 Node.js 版本中,如果 Promise 被拒绝且未得到处理,整个 Node.js 进程就会被中断。因此必要的时候你应该使用 try-catch:

const util = require('util')
async function main () {
 try {
  await new Promise((resolve, reject) => {
   reject(new Error('?'))
  })
 } catch (err) {
  // 在这里处理错误
  // 根据你的需要,有时候把错误直接再抛出也是可行的
 }
}
main()
 .then(console.log)
 .catch(console.error)

可是,使用 try-catch 可能会隐藏掉一些重要的异常,比如像系统错误,你可能更想把它再抛出来。关于在什么情况下你应该将错误再次抛出,我强烈建议你去读一下 Eran 的这篇文章。

3.4 更为复杂的流程控制

Caolan McMahon 的 async 是一个出现较早的用于 Node.js 中异步流程控制的库。它提供了一些进行异步操作控制的帮助工具,比如:

mapLimit,
filterLimit,
concatLimit,

以及 priorityQueue。

如果你不打算重新发明轮子,不想把同样的逻辑自己再实现一遍,并且愿意信赖这个经过实践检验的、每月下载量高达 5000 万的库,你可以结合 util.promisify 简单地重用这些函数:

const util = require('util')
const async = require('async')
const numbers = [
 1, 2, 3, 4, 5
]
mapLimitAsync = util.promisify(async.mapLimit)
async function main () {
 return await mapLimitAsync(numbers, 2, (number, done) => {
  setTimeout(function () {
   done(null, number * 2)
  }, 100)
 })
}
main()
 .then(console.log)
 .catch(console.error)

总结

以上所述是小编给大家介绍的在 Node.js 中使用 async 函数的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JavaScript 高效运行代码分析
Mar 18 Javascript
javascript检测页面是否缩放的小例子
May 16 Javascript
jQuery动画效果-fadeIn fadeOut淡入浅出示例代码
Aug 28 Javascript
javascript ajax的5种状态介绍
Aug 18 Javascript
jquery中页面Ajax方法$.load的功能使用介绍
Oct 20 Javascript
jquery控制表单输入框显示默认值的方法
May 22 Javascript
通过BootStrap实现轮播图的实际应用
Sep 26 Javascript
canvas实现图片根据滑块放大缩小效果
Feb 24 Javascript
javascript 动态生成css代码的两种方法
Mar 17 Javascript
详解JavaScript按概率随机生成事件
Aug 02 Javascript
jQuery 实现DOM元素拖拽交换位置的实例代码
Jul 14 jQuery
Vue proxyTable配置多个接口地址,解决跨域的问题
Sep 11 Javascript
JS实现的数组去除重复数据算法小结
Nov 17 #Javascript
bootstrap轮播模板使用方法详解
Nov 17 #Javascript
浅谈Node模块系统及其模式
Nov 17 #Javascript
基于Bootstrap表单验证功能
Nov 17 #Javascript
js 毫秒转天时分秒的实例
Nov 17 #Javascript
Bootstrap table使用方法汇总
Nov 17 #Javascript
Vue.js与 ASP.NET Core 服务端渲染功能整合
Nov 16 #Javascript
You might like
WinXP + Apache +PHP5 + MySQL + phpMyAdmin安装全功略
2006/07/09 PHP
首页四格,首页五格For6.0(GBK)(UTF-8)[12种组合][9-18][版主安装测试通过]
2007/09/24 PHP
php 时间计算问题小结
2009/01/04 PHP
php数据库备份还原类分享
2014/03/20 PHP
PHP Callable强制指定回调类型的方法
2016/08/30 PHP
Laravel中前端js上传图片到七牛云的示例代码
2017/09/04 PHP
ThinkPHP实现转换数据库查询结果数据到对应类型的方法
2017/11/16 PHP
Laravle eloquent 多对多模型关联实例详解
2017/11/22 PHP
php爬取天猫和淘宝商品数据
2018/02/23 PHP
弹出广告特效(一个IP只弹出一次)的代码
2007/07/27 Javascript
腾讯的ip接口 方便获取当前用户的ip地理位置
2010/11/25 Javascript
jQuery.extend 函数详解
2012/02/03 Javascript
禁止iframe页面的所有js脚本如alert及弹出窗口等
2014/09/03 Javascript
js实现文字跟随鼠标移动而移动的方法
2015/02/28 Javascript
js实现当前输入框高亮显示的方法
2015/08/19 Javascript
bootstrap模态框嵌套、tabindex属性、去除阴影的示例代码
2017/10/17 Javascript
JS处理数据四舍五入(tofixed与round的区别详解)
2017/10/26 Javascript
JavaScript自执行函数和jQuery扩展方法详解
2017/10/27 jQuery
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
2018/09/15 Javascript
利用python打开摄像头及颜色检测方法
2018/08/03 Python
Python查找最长不包含重复字符的子字符串算法示例
2019/02/13 Python
python实现将视频按帧读取到自定义目录
2019/12/10 Python
Django通用类视图实现忘记密码重置密码功能示例
2019/12/17 Python
python中pow函数用法及功能说明
2020/12/04 Python
详解CSS中iconfont的使用
2015/08/04 HTML / CSS
英国计算机商店:Technextday
2019/12/28 全球购物
世界上最好的野生海鲜和有机食品:Vital Choice
2020/01/16 全球购物
公司前台接待岗位职责
2013/12/03 职场文书
施工安全协议书
2013/12/11 职场文书
老师自我鉴定范文
2013/12/25 职场文书
《颐和园》教学反思
2014/02/26 职场文书
医院志愿者活动总结
2015/05/06 职场文书
变长双向rnn的正确使用姿势教学
2021/05/31 Python
python playwright 自动等待和断言详解
2021/11/27 Python
如何使用SQL Server语句创建表
2022/04/12 SQL Server
Python 操作pdf pdfplumber读取PDF写入Exce
2022/08/14 Python