前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库async 函数同步错误
JavaScript异步与 Promise

async 函数里直接 throw 一个错误,调用方会怎么收到?

async 函数里哪怕是第一行就 throw,调用方也不会立刻收到异常。错误会被装进返回的 Promise,变成 rejected 状态,只能用 await 加 try/catch 或者 .catch() 接住。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#异步与 Promise#异步编程
先看核心答案读代码示例
理解线索

同一个错误,三种去向

  1. async 函数体throw 被捕获,返回的 Promise 变成 rejected
  2. 普通函数返回 Promisereturn 之前的 throw 会同步抛给调用方
  3. setTimeout 回调跑在另一个任务里,错误变成全局未捕获异常

没人处理的 rejected Promise 不会让代码停下,只会在控制台报一条 Uncaught (in promise),浏览器里还会触发 unhandledrejection 事件,很容易被忽略。

核心回答

先记住这个答案

async 函数里的同步 throw 不会冒泡给调用方。引擎会把整个函数体包在一层「捕获」里,函数体一旦抛错,返回的 Promise 就变成 rejected,错误对象就是拒绝原因。调用本身永远正常返回一个 Promise,所以要么 await 它再用 try/catch,要么在后面接 .catch(),否则这个错误就成了未处理的拒绝。

  • async 函数不会同步抛错,错误都变成 rejected Promise
  • 调用时不 await,外层 try/catch 接不住
  • 参数默认值里出的错也会变成 rejected
  • setTimeout 回调里抛的错不归这个 Promise 管

先看代码和输出

下面这个函数第一行就抛错,看看调用方什么时候、以什么形式拿到它。

第一行就 throw 的 async 函数JavaScript
async function load() {
  console.log('进入函数')
  throw new Error('参数不合法') // 同步抛错
}

const p = load() // 这一行不会抛异常
console.log('拿到 Promise', p instanceof Promise)
p.catch((err) => console.log('catch 到', err.message))
console.log('同步代码结束')
查看输出与解释
进入函数
拿到 Promise true
同步代码结束
catch 到 参数不合法

进入函数 是同步打印的,说明函数体确实立刻执行了。但 throw 之后调用方没有崩,而是拿到了一个 Promise。.catch() 的回调是微任务,要等同步代码跑完才执行,所以 catch 到 排在最后。 你可以把 async 函数想成自带一个 try/catch 的包装:try 里跑函数体,catch 里调用 reject(err)。这层包装连参数默认值都罩住了,比如 async function f(a = b.c) {} 在 b 是 undefined 时,也是返回 rejected Promise,而不是同步报 TypeError。

为什么外层 try/catch 接不住

同步的 try/catch 只能接住「这一行执行过程中抛出来」的异常。调用 async 函数这一行从来不抛异常,它只是返回了一个会失败的 Promise。

不 await 和 await 的对比JavaScript
async function load() {
  throw new Error('boom')
}

let p
try {
  p = load() // 没有 await,这里不抛
  console.log('try 里一切正常')
} catch (err) {
  console.log('同步 catch', err.message) // 不会执行
}
p.catch((err) => console.log('只有 .catch 接得住', err.message))

async function main() {
  try {
    await load() // await 把 rejected 转成这一行的异常
  } catch (err) {
    console.log('await 后 catch', err.message)
  }
}
main()
查看输出与解释
try 里一切正常
只有 .catch 接得住 boom
await 后 catch boom

await 的作用是:Promise 成功就取出值,失败就在当前这一行把拒绝原因重新抛出来。所以 try/catch 必须和 await 配合,缺了 await 的话,try 只包住了一次成功的函数调用。

和普通函数返回 Promise 的区别
普通函数 function load() { if (!id) throw new Error('缺 id'); return request(id) } 就不一样了。return 之前的 throw 会同步抛给调用方,而 request 里的失败走 Promise。调用方要同时写 try/catch 和 .catch() 才能兜住两种错误。把它改成 async function,两种错误就统一成 rejected Promise,这是很多团队要求「返回 Promise 的函数一律写成 async」的原因。
回答前,多想一步

容易答错的地方

以为 async 函数里所有错误都会进 Promise
只有函数体同步执行的部分和 await 链上的错误才会。在函数里写 setTimeout(() => { throw err }),回调是在另一个宏任务里执行的,那时 async 函数早就返回了,这个错误会变成全局的未捕获异常,返回的 Promise 根本不知道。要让它进 Promise,得先把 setTimeout 包成 Promise 再 await。
以为「同步抛错」时后面的调用方代码不会执行
普通函数同步抛错,调用处后面的代码确实不会执行。但 async 函数不是这样,调用处后面的同步代码照常跑完,错误要等到微任务阶段才被 .catch() 处理。示例里 同步代码结束 先于 catch 到 打印就是这个原因。
把 return promise 写在 try 里以为能接住
在 async 函数里写 try { return request() } catch {} 时,request() 的拒绝不会进这个 catch,因为 return 直接把 Promise 交出去了,函数已经离开了 try。要接住得写成 return await request()。
试着用自己的话回答

面试官还会怎么问?

没人处理的 rejected Promise 会怎样?

浏览器会在控制台打印 Uncaught (in promise),并在 window 上触发 unhandledrejection 事件,可以在这里统一上报。Node.js 从 15 开始默认会因为未处理的拒绝直接让进程退出,所以服务端代码里漏写 .catch() 后果更严重。

new Promise 的执行器里 throw 会怎样?

和 async 函数一样,执行器里的同步 throw 会被捕获,Promise 变成 rejected。但执行器里异步回调抛的错同样接不住,只能在回调里自己调用 reject。

怎么让 async 函数的参数校验同步报错?

把校验放到外面一层普通函数里:先同步检查参数,不合法就直接 throw,合法再调用内部的 async 函数并返回它的 Promise。这样调用方写错参数时立刻就能在调用栈上看到错误,不用等到微任务阶段。

从一道题,走向一组知识

把知识连起来

异步与 Promise

async 函数里 return 一个值,外面拿到的是值还是 Promise?

async 函数 return 的值会变成 fulfilled,throw 的错误会变成 rejected,是同一套包装规则的两面。

异步与 Promise

forEach 里写 async/await,为什么不会一个一个按顺序等?

在 forEach 里调 async 回调时,每个回调的错误都变成没人接的 rejected Promise,外层 try/catch 一个都抓不到。

参考资料

  • JavaScript:异步与 Promise参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 先看代码和输出
  3. 为什么外层 try/catch 接不住
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑