如何优雅地处理Async/Await的异常?
译者按: 使用.catch()
来捕获所有的异常
本文采用意译,版权归原作者所有
async/await 中的异常处理很让人混乱。尽管有很多种方式来应对async 函数的异常,但是连经验丰富的开发者有时候也会搞错。
假设你有一个叫做run()
的异步函数。在本文中,我会描述 3 种方式来处理run()
的异常情形: try/catch
, Go 语言风格, 函数调用的时候使用 catch()
(即run().catch()
)。 我会跟你解释为什么其实几乎只需要catch()
就足够。
try/catch
当你第一次使用async/await
, 你可能尝试使用try/catch
将每一个 async 操作包围起来。如果你await
一个被 reject 的 Promise,JavaScript 会抛出一个可以被捕获的错误。
run(); async function run() { try { await Promise.reject(new Error("Oops!")); } catch (error) { error.message; // "Oops!" } }
try/catch
能够捕获非异步的异常。
run(); async function run() { const v = null; try { await Promise.resolve("foo"); v.thisWillThrow; } catch (error) { // "TypeError: Cannot read property 'thisWillThrow' of null" error.message; } }
所以,只需要将所有的代码逻辑都用 try/catch
包围起来就可以搞定?也不完全正确。下面的代码会抛出unhandled promise rejection. await
将一个被拒绝的 promise 转换为可捕获的错误,但是 return
不行。
run(); async function run() { try { // 注意这里是return,不是await return Promise.reject(new Error("Oops!")); } catch (error) { // 代码不会执行到这里 } }
也不可能使用 return await
来绕开。
还有一个缺点就是使用了try/catch
之后,就很难用.
的语法来进行 Promise 链式组合了。
使用 Go 的语法
另一个常见的方式就是使用then()
将一个本来需要用catch()
来捕获并处理的 Promise 转换为普通的 Promise。然后像 Go 语言中一样,使用if(err)
来处理异常。
run(); async function throwAnError() { throw new Error("Oops!"); } async function noError() { return 42; } async function run() { // The `.then(() => null, err => err)` 来匹配正常/异常的情况。如果正常情况,返回`null`;如果异常,返回`err` let err = await throwAnError().then(() => null, err => err); if (err != null) { err.message; // 'Oops' } err = await noError().then(() => null, err => err); err; // null }
如果你真的想要同时返回 error 和正确的值,你可以完全假装在用 Go 语言。
run(); async function throwAnError() { throw new Error("Oops!"); } async function noError() { return 42; } async function run() { // The `.then(v => [null, v], err => [err, null])` pattern // 你可以使用数组解构来匹配err和返回值 let [err, res] = await throwAnError().then( v => [null, v], err => [err, null] ); if (err != null) { err.message; // 'Oops' } err = await noError().then(v => [null, v], err => [err, null]); err; // null res; // 42 }
使用 Go 语言风格的错误处理并不能摆脱return
无法捕获的情况。而且还让整个代码更加的复杂,如果忘记if(err != null)
,就会出问题。
总的来说,有两大缺点:
- 代码极度重复,每一个地方都少不了
if (err != null)
,真的很累,而且容易漏掉; -
run()
函数中的非异步的错误也无法处理;
总的来说,它并没有比try/catch
好多少。
在函数调用的时候使用catch()
try/catch
和 Go 语言风格的异常处理都有各自的使用场景,但是处理所有异常最好的方法是在run()
函数的后面使用catch()
,像这样:run().catch()
。换句话说,用一个catch()
来处理run
函数中的所有错误,而不是针对run
里面的每一种情况都去写代码做相应的处理。
run() .catch(function handleError(err) { err.message; // Oops! }) // 在handleError中处理所有的异常 // 如果handleError出错,则退出。 .catch(err => { process.nextTick(() => { throw err; }); }); async function run() { await Promise.reject(new Error("Oops!")); }
记住,async 函数总是返回 promise。只要函数中有异常,Promise 会 reject。而且,如果一个 async 函数返回的是一个 reject 的 Promise,那么这个 Promise 依然会继续被 reject。
run() .catch(function handleError(err) { err.message; // Oops! }) .catch(err => { process.nextTick(() => { throw err; }); }); async function run() { // 注意:这里使用了return,而不是await return Promise.reject(new Error("Oops!")); }
为什么使用run().catch()
而不是将整个run()
函数用try/catch
包起来呢?我们首先来考虑一个情况:如果try/catch
的catch
部分有异常,我们应该如何处理呢?只有一个方法:在catch
里面接着使用try/catch
。所以,run().catch()
的模式使得异常处理变得非常简洁。
总结
我们最好是全局的有一个 errorHandler 来处理那些没有考虑到的异常,比如使用run().catch(handleError)
,而不是在run()
函数里面所有可能出错的地方加上try/catch
。
关于Fundebug
Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!
版权声明
转载时请注明作者 Fundebug以及本文地址:https://blog.fundebug.com/2019/07/24/async-await-error-handling-in-js/
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Protocol Buffers 简介
本文档的Protocol Buffer 的中文文档使用的是 Asciidoctor 进行编排的 http://docs.ossez.com/protocol-buffers-docs/index.html(本 WIKI 中的内容将会与在线发布的版本同步) Google Protocol Buffer(简称Protobuf)是Google公司内部的混合语言数据标准,目前已经正在使用的有超过48,162种报文格式定义和超过12,183个.proto文件。 如果你在互联网上搜索的话,你可能会看到下面的说明文字。 Google Protocol Buffer(简称Protobuf)是Google公司内部的混合语言数据标准,目前已经正在使用的有超过48,162种报文格式定义和超过12,183个.proto文件。 他们用于RPC系统和持续数据存储系统。 Protocol Buffers是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。 它很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 目前提供了C++...
- 下一篇
JSBridge框架解决通信问题实现移动端跨平台开发
一、跨平台开发是趋势 目前主流的移动端平台主要是Android和iOS,为了尽可能复用代码和节省开发成本,各大巨头都开发了自己的跨平台框架,比如Facebook的React-Native、阿里的Weex、Cordova,以及今年Google开发者大会上介绍的Flutter框架。这些框架各有优缺点,但是到目前为止都没有大规模地推广开来,在我看来主要有以下几个原因: 1、开发者生态圈还不够成熟 RN是三大跨平台框架中关注人最多、生态最活跃的框架,但是到目前为止也没有到1.0版本(最新的release是0.57.8),更别说作为后来者的Weex和Flutter了。生态不成熟,意味着开发文档少,可以使用的开源控件少,比如在RN上想做一个最基本的下拉刷新和上拉加载更多的listview都比较费劲。Weex已经贡献给Apache,很久没有更新rel
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Windows10,CentOS7,CentOS8安装Nodejs环境