如何优雅地处理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/
如何优雅地处理Async/Await的异常?的更多相关文章
- 微信小程序捕获async/await函数异常实践
背景 我们的小程序项目的构建是与web项目保持一致的,完全使用webpack的生态来构建,没有使用小程序自带的构建功能,那么就需要我们配置代码转换的babel插件如Promise.Proxy等:另外, ...
- .NET异步操作学习之一:Async/Await中异常的处理
以前的异常处理,习惯了过程式的把出现的异常全部捕捉一遍,然后再进行处理.Async/Await关键字出来之后的确简化了异步编程,但也带来了一些问题.接下来自己将对这对关键字进行学习.然后把研究结果放在 ...
- 如何优雅的处理 async/await 异常
参考链接:https://cloud.tencent.com/developer/article/1470715 参考链接:https://www.jianshu.com/p/2935c0330dd2
- 优雅地 `async/await`
async/await 虽然取代了回调,使用类似同步的代码组织方式让代码更加简洁美观,但错误处理时需要加 try/catch. 比如下面这样,一个简单的 Node.js 中使用 async/await ...
- Vuex结合 async/await 优雅的管理接口请求
先看看 async/await 的语法 async 函数返回一个 Promise 对象 async 函数内部 return 返回的值.会成为 then 方法回调函数的参数. 1 2 3 4 async ...
- 进阶篇:以IL为剑,直指async/await
接上篇:30分钟?不需要,轻松读懂IL,这篇主要从IL入手来理解async/await的工作原理. 先简单介绍下async/await,这是.net 4.5引入的语法糖,配合Task使用可以非常优雅的 ...
- Async/Await替代Promise的6个理由
译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...
- promise async await使用
1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...
- callback vs async.js vs promise vs async / await
需求: A.依次读取 A|B|C 三个文件,如果有失败,则立即终止. B.同时读取 A|B|C 三个文件,如果有失败,则立即终止. 一.callback 需求A: let read = functio ...
随机推荐
- Django2.0中基于正则表达式的路由机制(一)
1. 在urls.py的文件中导入操作正则表达式的方法:(新版的Django是使用path方法对URL进行路由分配) from django.contrib import admin from dj ...
- 5-5 可视化库Seaborn-多变量分析绘图
Parameters:¶ 参数 解释 变量 x,y,hue 数据集变量 变量名 date 数据集 数据集名 row,col 更多分类变量进行平铺显示 变量名 col_wrap 每行的最高平铺数 整 ...
- windows宿主机和docker容器设置挂载共享文件夹
docker容器内的程序经常需要访问.调用宿主机目录中的数据,每次都要导入导出非常麻烦费力. 接下来,一步步实现将宿主机的指定文件夹挂载到docker容器中. 1. 打开Oracle VM Vitua ...
- C++踩坑——用memset对vector进行初始化
在一段程序中,使用memset对vector进行了初始化,然后得到了错误的结果.找这个bug花费了很长时间. vector中有其自身的结构,不能单纯的按字节进行初始化.使用memset对vector进 ...
- 一套从alpine基本镜像到node8.16.2的全套dockerfile
这个花了点时间,可以正式跑起来了. 加了常用的工具及中文时区,非root帐号. 除了pm2,其它的module放到应用程序本身的node_modules目录下来实现的. 一,3rd_part/node ...
- 忘记IBM服务器的登录IP
问题描述: 一台服务器安装了winserver2003系统,经过漫长的加电启动,能进入到win2003的登录界面,提示ctrl+alt+del登录界面,但是发现键盘失灵了,无法键入ctrl+alt+d ...
- Tensorflow之变量赋值输出1+2+3+4+5+6+7+8+...
一.导入tensorflow import tensorflow as tf 二.定义计算图 (1)常量初始化 constant_name = tf.constant(value) (2)变量初始化 ...
- 理解docker镜像
镜像是用来启动容器的只读模板,是容器启动所需要的rootfs,类似于虚拟机所使用的镜像. 列出本机镜像 [root@localhost ~]# docker imagesREPOSITORY TAG ...
- day53_9_17 django数据库表关联,路由和视图
一.数据库的关系建立. 在原生的数据库语句中,建立表与表之间的联系,就是添加一个字段,将联系的表的id值添加到该字段中. django所作的也就是这些. 以图书管理系统为例,图书管理系统有四张表:书籍 ...
- 使用python发邮件:
import smtplibfrom email.mime.text import MIMETextfrom email.utils import formataddr#定义发送的内容:msg = M ...