← 返回题目列表

JavaScript 错误处理有哪些方式?同步异常和异步异常有什么区别?

中等 第 20 / 29 题 更新于 2026/07/29
JavaScript异常处理Promise

简化版

同步代码可以用 try...catch 捕获异常;Promise 异步错误用 .catchasync/await 外层的 try...catch 捕获;事件回调、定时器、跨任务异常不能被外层同步 try...catch 捕获。

工程上要区分预期错误和程序错误,既要给用户友好反馈,也要记录日志、保留上下文并避免吞掉错误。

详细版

同步异常:

try {
  JSON.parse("{bad json}");
} catch (err) {
  console.log(err.message);
}

Promise 异常:

fetch("/api/user")
  .then(res => res.json())
  .catch(err => report(err));

async/await

async function loadUser() {
  try {
    const res = await fetch("/api/user");
    return await res.json();
  } catch (err) {
    report(err);
    throw err;
  }
}

定时器里的错误不会被外层同步 try...catch 捕获,因为它在另一个任务中执行。

完整版教学

一、错误处理先分同步和异步

同步异常发生在当前调用栈中,try...catch 能捕获。

异步异常发生在未来某个任务、微任务或回调里,外层同步调用栈已经结束,不能靠外层 try...catch 兜住所有情况。

try {
  setTimeout(() => {
    throw new Error("timer error");
  }, 0);
} catch (err) {
  console.log("will not run");
}

面试答错误处理时,先说“调用栈边界”,很多异步捕获问题就清楚了。

二、try…catch 适合当前调用栈

try...catch 适合解析、计算、同步 API 调用等当前栈内错误。

function parseConfig(text) {
  try {
    return JSON.parse(text);
  } catch (err) {
    return { theme: "default" };
  }
}

但不要把过大的业务流程全部包起来后静默失败。捕获后应知道怎么恢复,或者继续向上抛。

三、Promise 的错误传播

Promise 链中的 throw 会变成 rejected。

Promise.resolve()
  .then(() => {
    throw new Error("bad");
  })
  .catch(err => console.log(err.message));

.catch 本质上相当于只处理 rejected 的 .then。如果 .catch 里不重新抛出,后续链会回到 fulfilled。

Promise.reject(new Error("x"))
  .catch(() => "fallback")
  .then(value => console.log(value)); // fallback

四、async/await 的捕获方式

await 一个 rejected Promise 时,会像同步 throw 一样进入 catch

async function main() {
  try {
    await Promise.reject(new Error("fail"));
  } catch (err) {
    console.log("caught");
  }
}

如果忘记 await,错误不会被当前 try...catch 捕获。

try {
  Promise.reject(new Error("lost"));
} catch (err) {
  console.log("not caught");
}

五、全局兜底不是业务处理

浏览器里常见兜底:

入口作用
window.onerror捕获部分未处理同步错误
unhandledrejection捕获未处理 Promise rejection
框架错误边界捕获框架渲染层错误

兜底适合上报和降级,不适合当作正常业务流程。

六、预期错误和程序错误要分开

预期错误是业务可以理解的失败,比如表单校验失败、接口 401、库存不足。

程序错误是代码缺陷,比如空指针、类型假设错误、状态不一致。

error
  ├─ expected: show message / retry / redirect
  └─ bug: report / isolate / fail fast

把所有错误都弹一个“系统繁忙”会让排障困难,也会伤害用户体验。

七、常见误区与追问

  • 误区:外层 try…catch 能捕获 setTimeout 里的 throw。 定时器回调在新的任务中执行,外层栈已经结束。
  • 误区:Promise 里 catch 后一定还会失败。 如果不重新抛出,链会恢复为 fulfilled。
  • 误区:async 函数里的错误都会自动被处理。 调用方仍然要 await.catch
  • 误区:全局错误监听能替代局部错误处理。 全局兜底主要负责上报和最后降级。
  • 追问:为什么不能随便吞掉错误? 会让调用方误以为成功,导致状态继续被污染。
  • 追问:错误上报应该带哪些上下文? URL、用户操作、接口、组件栈、版本号和关键参数。

八、加强记忆

按边界记:

same stack       -> try...catch
promise chain    -> .catch
await rejection  -> try...catch around await
global fallback  -> onerror / unhandledrejection

回答时先分同步和异步,再讲 Promise 与 async/await,最后补工程实践:预期错误给恢复路径,程序错误要上报,不要静默吞错。