async await 的错误处理方法

本文介绍了JavaScript中处理async/await错误的三种方法:1) 使用try/catch捕获同步和异步错误,防止控制台无报错导致问题难以定位;2) 通过Golang风格的错误处理,将rejected的promise转换为success并返回错误信息;3) 在async函数外使用catch处理所有错误。强调了确保处理所有错误的重要性,并提供了相关参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值