async await 的错误处理方法

news/2024/7/10 22:46:02 标签: javascript, es6

1. try/catch

es6 的初学者必须知道的捕获错误的方法,因为它是相对来说最保险的,既可以捕获同步错误也可以捕获异步错误。

捕获异步错误:

run();

async function run() {
  try {
    await Promise.reject(new Error('Oops!'));
  } catch (error) {
    error.message; // "Oops!"
  }
}

捕获同步错误:

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;
  }
}

新手用 async/await 容易犯的错误之一,是忘记捕获错误,这是大忌,它会导致出现“S级”的异常:**控制台既没有报错,期待的效果也没有work,会导致很难定位问题。**以下的示例取自于一个个人比较常见的情景:

import api from './api.js' // 引入了一个请求库

run();

async function run() {
	const res = await axios.get('xxxx')  
  // 期待打印出请求结果,但因为没有做错误处理,
  // 导致当网络请求出错时,这一行既没有走到,
  // 控制台也没有抛出 Error,会很难定位问题
  console.log(res)
}

但这不意味着 try/catch 就万无一失了。try 代码块中被 return 的 rejected 的 promise 是无法被捕获到的:

run();

async function run() {
  try {
    // 注意: 这是一个 `return` ,不是 `await`
    return Promise.reject(new Error('Oops!'));
  } catch (error) {
    // 不会运行
  }
}

其实,将 return 改成 return await 可以解决这个问题的,但容易遗漏。

2. 以Golang 风格捕获错误

.then() 将 rejected 状态的 promise 转成成功状态的 promise 并返回错误,可以用 if (err) 来检查是否出错

run();

async function throwAnError() {
  throw new Error('Oops!');
}

async function noError() {
  return 42;
}

async function run() {
  // `.then(() => null, err => err)` 模式下,
  // 当错误发生时返回一个 error ,否则返回 null
  let err = await throwAnError().then(() => null, err => err);
  if (err != null) {
    err.message; // 'Oops'
  }

  err = await noError().then(() => null, err => err);
  err; // null
}

如果同时需要成功的值和错误信息,可以像写 Golang 一样写 Javascript

run();

async function throwAnError() {
  throw new Error('Oops!');
}

async function noError() {
  return 42;
}

async function run() {
  // `.then(v => [null, v], err => [err, null])` 的模式
  // 让你能用数组的形式同时获取 error 和 result
  let [err, res] = await throwAnError().
    then(v => [null, v], err => [err, null]);
  if (err != null) {
    err.message; // 'Oops'
  }

  [err, res] = await noError().
    then(v => [null, v], err => [err, null]);
  err; // null
  res; // 42
}

如果喜欢这种写法,还可以对它做一个封装:

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

// use
import to from './to.js';

run();

async function throwAnError() {
  throw new Error('Oops!');
}

async function noError() {
  return 42;
}

async function run() {
  // 用 to 来曝光promise
  let [err, res] = await to(throwAnError())
  if (err != null) {
    err.message; // 'Oops'
  }

  [err, res] = await to(noError())
  err; // null
  res; // 42
}

如果需要拓展 error 的信息、是否 errorFirst,甚至可以做更复杂的封装:

// to.js
function to (promise, errorProps = {}, errorFirst = true) {
	return promise.then((data) => errorFirst ? [null, data] : [data, null])
			  .catch(err => {
				  if(errorProps) Object.assign(err, errorProps)
				  errorFirst ? [err, null] : [null, err]
			  })
  }

3. catch()

try/catch 和 Golang风格的错误处理都有它们的用途,但是,要确保你处理了 run() 函数里所有错误的、最好的方法,是用 run().catch()(前提:run是异步函数)。换句话说,在调用函数时就处理错误,而不是单独处理每个错误。

run().
  catch(function handleError(err) {
    err.message; // Oops!
  }).
	// 处理 `handleError()`中的任何错误:如果出错则杀死进程
  catch(err => { process.nextTick(() => { throw err; }) });

async function run() {
  await Promise.reject(new Error('Oops!'));
}

记住,所有的 async 函数总是返回 promises。如果函数内有任何未捕获的错误发生了,这个 promise 也会 rejected。如果你的 async 函数返回了一个 rejected 的 promise,返回的 promise 也会 rejected 。

run().
  catch(function handleError(err) {
    err.message; // Oops!
  }).
	// 处理 `handleError()`中的任何错误:如果出错则杀死进程
  catch(err => { process.nextTick(() => { throw err; }) });

async function run() {
  // 注意:这是一个 `return` ,不是 `await`
  return Promise.reject(new Error('Oops!'));
}

总结

try/catchGolang风格.catch()
优点- 最保险的,可捕获同步、await的异步错误
- 流程控制准确
- 优雅
- 能捕获异步函数体内的同步和抛出的异步错误
缺点- 无法捕获 try块中,被return 的异步错误(可用return await解决)
- 不能优雅地对错误分别处理
- 不适用于流程中不需要中断的错误
- 无法避免catch本身抛出异常且不好处理
- 大量重复 if(err !== null),可能遗漏

- 无法捕获外部函数体内的同步错误
- 只适用于有 .catch() 方法的异步函数
场景非常适合简单流程、不希望出错后影响后续执行流程的情景适合链路长、较复杂的异步流程;适合于单个环节出错不影响流程适合处理期望外的错误,及时处理;适合给.catch() 做兜底

参考资料

http://thecodebarbarian.com/async-await-error-handling-in-javascript.html

https://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/

https://github.com/huruji/blog/issues/61


http://www.niftyadmin.cn/n/913953.html

相关文章

基于PB的Windows CE模拟环境的配置及调试

嵌入式系统被用于越来越多的场合,不仅仅是PDA和手持式电脑,许多手机、游戏机、数字播放器、数码相机和车载电脑也开始使用标准的CE嵌入式系统,甚至连保守的传统AV设备--DVD和MPEG-4 Player也不例外。例如,多普达的手机&#xff0c…

20171019课堂测试

第二周测试 用实验楼重新建立add.c,sub.c,div.c,main.c等文件,敲代码时间比较久,main.c文件代码敲错了,导致一直error。课下装上虚拟机,重新运行了一遍,这样以后文件可以直接保存在Linux系统中。 4-静态库的测试 除了m…

在github上写脚注的方法 markdown

在github上写脚注 Writting Footnotes In Github 现状 目前Github原生的issue和markdown预览不支持标准md格式的脚注(csdn也不支持),像以下这样 文章内容[^1][^1]: 脚注1的说明替代方案 可以用锚点的特性上标标签实现相似的功能&#xf…

Windows平台安装Bugzilla(上)

Bugzilla是一个开源、免费的Bug跟踪管理工具,对软件开发过程的错误跟踪。基于Web浏览和邮件通知。系统存储BUG采用的也是开源、免费的数据库MySQL。在Bugzilla的官方网站上有单独的文档说明如何在Windows下安装Bugzilla。读者也可以参考那里的说明:http:…

JavaScript的数值存储的探析与应用

文章目录JavaScript的数值存储探析学1. 浮点数的存储规则2. Number对象上的特殊值MAX_SAFE_INTEGERMAX_VALUE3. 特殊值的存储学以致用案例分析1.1 精度丢失1.2 大数危机1.3 toFixed()对于小数最后一位为5时进位不正确问题2. 解决方案“修复” 0.10.2 0.32.2 修复数据展示修复 …

mysql8 Expression #16 of SELECT list is not in GROUP BY clause and contains nonaggregated column

出错 Expression #16 of SELECT list is not in GROUP BY clause and contains nonaggregated column xxx.xxxx.xxxid which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_modeonly_full_group_by /etc/my.conf sql_modeSTR…

跨站脚本攻击漏洞

漏洞资料:目前所涉及版本的论坛短信内容处没有做好细致的过滤,导致跨站脚本漏洞攻击的产生,使得用户可以得到管理员的COOKIE 信息.从而进一步危害论坛.漏洞利用:在撰写短消息处正文内容上填上如下利用代码:一,网页木马:<script>document.write(<iframe src"http:…