try...catch 能捕获哪些异步错误?forEach 里 await 为什么不按顺序等待?

先理解原理和示例,再练习完整口述与追问。

try...catch 只捕获 try 块同步执行期间抛出的错误;稍后在定时器、事件回调中抛错捕获不到;Promise 必须在 async 函数中 await 后才能被 catch。forEach 不等待回调返回的 Promise,应改用 for...of 串行或 Promise.all 并发。

try { setTimeout(() => { throw err }) } catch {} 中,try 块只负责登记定时器,回调在之后的任务里执行,那时 try 早已结束。try { fetchData() } catch {} 只拿到了一个 Promise,拒绝发生在之后,不会进入 catch;try { await fetchData() } catch {} 中,await 让 async 函数暂停,Promise 拒绝时在 await 处抛出,因此能捕获。

Array.prototype.forEach 同步调用回调并忽略返回值。async 回调会立刻返回 Promise,forEach 不等它,也不汇总它的错误,于是循环后的代码先执行,拒绝变成未处理的 Promise。

没有被局部捕获的错误,可以在全局兜底:同步错误和回调中的错误触发 window 的 error 事件,未处理的 Promise 拒绝触发 unhandledrejection;脚本、图片等资源加载失败的 error 事件不冒泡,需要在 window 上用捕获阶段监听。

js
async function saveAll(items) {
  for (const item of items) {
    await save(item) // 串行:前一个完成后再保存下一个,任何失败都会抛到调用方
  }
  await Promise.all(items.map(item => save(item))) // 并发:全部发出,等全部成功或第一个失败
}

如果把第一段改成 items.forEach(async item => await save(item)),函数会立刻结束,保存失败也不会被 saveAll 的调用方捕获。

全局 unhandledrejection 只是兜底上报,不能代替业务中的错误状态和重试;Promise.all 第一个失败就拒绝,但其他请求不会自动取消,需要配合 AbortController。

try...catch 只覆盖 try 块里同步执行的部分。定时器、事件回调里后来抛的错捕获不到;Promise 的拒绝只有在 async 函数里 await 它时才会在 await 处抛出,从而被 catch 捕获。forEach 会忽略回调返回的 Promise,不会等待也不会传播错误,所以需要顺序执行用 for...of 加 await,并发执行用 Promise.all 或并发控制。全局再用 error 和 unhandledrejection 做兜底上报。