node.js中async/await不用try/catch,使用error first或go语言方法处理异常


发布者 newghost  发布时间 1608344490110
关键字 Node.JS  编程技巧 

现在node.js一般使用async/await来处理回调嵌套地域。然后使用try/catch来捕获错误。

try/catch的好处

比如下面的例子,当不传参数时回调函数会抛出异常:

const util = require('util')

//传统回调函数
function task(...args) {
  //最后一个参数一般为回调,将其从数组中移出
  let cb = args.pop()
  //无参数时抛出错误
  if (args.length < 1) {
    cb(new Error('Wrong parameter'))
    return
  }

  //error first写法,没有错误时第一个参数传null
  cb(null, args)
}

//转化为promise方法,以便使用async/await
const taskAsync = util.promisify(task)

//声明一个async的箭头函数
;(async()=>{
  try {
    let [arg1, arg2] = await taskAsync(1, 2)
    await taskAsync()
  } catch(err) {
    console.log(err)
  }
})()

使用try/catch的好处是可以将很多可能的错误代码包在一个块中,然后只使用一个异常捕获。并且第一条执行语句并不再需要接收处理error。

但现实生活中,基本上需要对每一处异常需要单独处理,这样使用try/catch的代码就会显得非常臃肿,如 Dima 在 https://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/ 这篇博文中的例子:

async function asyncTask(cb) {
    try {
       const user = await UserModel.findById(1);
       if(!user) return cb('No user found');
    } catch(e) {
        return cb('Unexpected error occurred');
    }

    try {
       const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'});
    } catch(e) {
        return cb('Error occurred while saving task');
    }

    if(user.notificationsEnabled) {
        try {
            await NotificationService.sendNotification(user.id, 'Task Created');  
        } catch(e) {
            return cb('Error while sending notification');
        }
    }

    if(savedTask.assignedUser.id !== user.id) {
        try {
            await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you');
        } catch(e) {
            return cb('Error while sending notification');
        }
    }

    cb(null, savedTask);
}

这样的代码就显得过长

将error变成返回参数

上面的博文中提到,go语言使用类似javascript error first的原则,将异常变成了一个参数:

data, err := db.Query("SELECT ...")
if err != nil { return err }

博客中将promise方法进行了封装,将error返回成第一个参数:

// to.js
export default function to(promise) {
   return promise.then(data => {
      return [null, data];
   })
   .catch(err => [err]);
}

然后上面那段很多try/catch的代码就会转换成:

import to from './to.js';

async function asyncTask() {
     let err, user, savedTask;

     [err, user] = await to(UserModel.findById(1));
     if(!user) throw new CustomerError('No user found');

     [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) throw new CustomError('Error occurred while saving task');

    if(user.notificationsEnabled) {
       const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created'));  
       if (err) console.error('Just log the error and continue flow');
    }
}

结构上简洁了很多,但每一处await都多了一个中间方法to的调用。

方法改进

Dima 的方法其实可以稍微改进一下,我们可以借鉴util.promisify方法,将普通函数封装一下,做一个通用方法,将普通回调转化成将error始终放到第一个返回参数的promise方法:

const util = require('util')

function task(...args) {
  let cb = args.pop()
  if (args.length < 1) {
    cb(new Error('Wrong parameter'))
    return
  }

  cb(null, args)
}

//将参数函数转化成error first的promise方法
function promisify(fn) {
  let fnAsync = util.promisify(fn)

  return (...args)=>{
    return fnAsync(...args)
      .then(data=>{
        return [null, data]
      })
      .catch(err => [err])
  }
}

const taskNoTry = promisify(task)

;(async()=>{
  var [ err, args ] = await taskNoTry()
  if (err) return console.log(err)
  
  //数组参数解构
  var [ err, [arg1, arg2] ] = await taskNoTry(1, 2)
  if (err) return console.log(err)
  
  console.log(arg1, arg2)
})()

这种方法的一个缺陷是error放到了返回数组中,对数据解构略显奇怪。









 热门文章 - 分享最多
  1. 网站集成百度、Bing必应搜索引擎,在网页中实现站内全文搜索
  2. React Hooks入门教程九:在React中集成使用Vue实现数据双向绑定,手动配置Webpack和Babel
  3. node.js性能压力测试入门教程:wrk和loadtest安装使用
  4. JavaScript和node.js内存泄露的原因和避免方法及Chrome调试工具使用教程
  5. Html5网页中用JavaScript调用本地手机摄像头扫描识别微信二维码、条形码:postMessage跨域https传递扫码结果消息
  6. JavaScript设置对象属性只读不可修改、不可枚举、不可删除:Object.defineProperty
  7. CSS教程:图片使用混合模式和颜色叠加filter滤镜,改变PNG图标颜色
  8. CSS教程:如何设置自动显示隐藏scrollbar滚动条,自定义外观样式/宽度,附demo示例大全
  9. node.js通过Error.prepareStackTrace获取上层调用函数的文件名地址和行数位置
  10. JavaScript判断字符串是否为数字类型:Number.isInteger、isNaN、正则表达式比较

 相关阅读
  1. webpack前端项目调试环境安装入门:webpack.config.js禁用UglifyJs只合并JavaScript不压缩混淆代码
  2. node.js创建aria2代理服务器:使用net.socket转发rpc或http request请求,替换websocket
  3. request停止维护:用node.js实现http网页爬虫抓取,模拟ajax\post请求,大文件上传下载
  4. 用node.js在Windows或Linux平台上高性能解压/压缩zip、tar大文件,输出到文件或Stream流
  5. Node.JS中回调嵌套和async/await执行空函数性能效率对比测试
  6. Node.JS如何按顺序调用async函数,如何判断是否为async函数,在mocha中自动化测试async/await代码
  7. node.js将回调函数嵌套,用promise改造成async/await顺序执行:异常处理和返回多个参数
  8. 基于Node.JS和Electron编写的集成开发环境 VS Code,成为最受欢迎的IDE
  9. 使用node.js和oAuth2协议集成Github/LinkedIn第三方登录以OnceOA模块源码为例
  10. Node.JS发送http请求批量检查文件中的网页地址、服务是否有效可用

  开源的 OurJS
OurJS开源博客已经迁移到 OnceOA 平台。

  关注我们
扫一扫即可关注我们:
OnceJS

OnceOA