前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库Promise 链抛出异常
JavaScript异步与 Promise

then 回调里 throw 一个错误,后面的链会怎样?

then 回调里 throw 一个错误,不会让程序崩掉。这个错误会被自动接住,变成 then 返回的新 Promise 的拒绝原因,后面的成功回调全部跳过,直到遇到 catch。

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

回调抛错,新 Promise 被拒绝

  1. 自动捕获then 回调同步抛出的异常,成为新 Promise 的拒绝原因
  2. 沿链传递没有失败回调的 then 原样放行,直到遇到 catch
  3. 恢复链catch 正常返回后,后面的 then 继续执行

只有回调执行期间同步抛出的异常才会被捕获。回调里再开一个 setTimeout,在里面 throw,那个错误跟这条链没有任何关系。

核心回答

先记住这个答案

then 调用后会返回一个新的 Promise。回调执行时如果抛出异常,这个异常会被 Promise 自动捕获,新 Promise 变成 rejected,异常就是拒绝原因。于是后面的 then 成功回调都会被跳过,错误一路往下传,直到遇到 catch 或 then 的第二个参数。如果整条链都没有处理,就会变成未处理的拒绝。要注意,只有回调里同步抛出的异常才会被捕获。

  • then 回调里 throw 等于返回一个拒绝的 Promise
  • 后续 then 的成功回调全部跳过,直达 catch
  • 回调里异步抛出的错误接不住
  • 没有 catch 会触发 unhandledrejection

错误怎么沿链走下去

先看一条最常见的链:第一步抛错,看看第二步和 catch 之后会发生什么。

抛错、跳过、恢复JavaScript
Promise.resolve(1)
  .then((v) => {
    console.log('第一个 then:', v)
    throw new Error('boom') // 效果等同于 return Promise.reject(new Error('boom'))
  })
  .then(() => console.log('第二个 then')) // 被跳过
  .catch((err) => {
    console.log('catch:', err.message)
    return 'fallback' // 正常返回,链恢复成功
  })
  .then((v) => console.log('catch 之后:', v))
查看输出与解释
第一个 then: 1
catch: boom
catch 之后: fallback

第一个 then 抛错后,它返回的 Promise 以这个 Error 被拒绝。第二个 then 只给了成功回调,拿到的却是一个拒绝,于是它什么也不做,直接把同样的错误传给自己返回的 Promise。catch 接住错误,正常返回了 'fallback',链又恢复成成功,最后一个 then 拿到这个值。 可以理解为每个 then 回调都被 Promise 悄悄套了一层 try/catch:回调正常返回,就用返回值兑现新 Promise;回调抛错,就用错误拒绝新 Promise。这也是为什么 throw err 和 return Promise.reject(err) 在链上效果一样。

真实场景:一步出错,后面全部跳过

比如读取用户配置,链写成 loadConfig().then(JSON.parse).then(validate).then(render).catch(showError)。

哪一步出错都走同一个出口
服务器返回的内容不是合法 JSON,JSON.parse 就会抛出 SyntaxError。这个异常让那一步的 Promise 被拒绝,validate 和 render 都被跳过,直接进入 showError。validate 发现必填字段缺失时,主动 throw new Error('缺少 theme 字段'),也走同一个出口。 这就是链式写法的好处:你不需要在每一步检查上一步有没有成功,坏数据不会流到界面上,所有错误集中在末尾处理。

哪些错误接不住

自动捕获有一个边界:它只管回调执行期间同步抛出的异常。

回调里的异步 throwJavaScript
Promise.resolve()
  .then(() => {
    setTimeout(() => {
      throw new Error('later') // 这时 then 回调早已返回,新 Promise 已经兑现
    }, 0)
  })
  .catch(() => console.log('接不住')) // 不会执行,错误变成全局未捕获异常

then 回调执行时只是注册了一个定时器,然后正常返回 undefined,新 Promise 立刻就兑现了。一段时间后定时器里的 throw 发生在另一个调用栈里,跟这条链毫无关系,只会变成全局的未捕获异常。 正确的做法是把异步操作包成 Promise 并 return 出去,在里面失败时调用 reject,这样链才会等待它,失败也能被 catch 接到。

回答前,多想一步

容易答错的地方

以为外层 try/catch 能接住 then 里的错误
then 回调是在微任务里执行的,那时外层的 try 块早就执行完了。这种写法接不住任何东西,要么在链上加 catch,要么改成 await 放在 try 里面。
以为抛错之后后面的 then 都不会执行了
只是成功回调被跳过。一旦错误被 catch 处理掉并正常返回,后面的 then 又会照常执行,拿到的是 catch 的返回值。
试着用自己的话回答

面试官还会怎么问?

不用 throw,怎么在 then 里主动让链失败?

返回一个拒绝的 Promise,也就是 return Promise.reject(new Error('xxx'))。效果和 throw 一样。一般更推荐直接 throw,写法短,而且在 async 函数里也是同样的写法。

throw 一个字符串而不是 Error 对象,行不行?

能正常传递,catch 拿到的就是这个字符串。但字符串没有调用栈,出了问题很难定位是哪一行抛的,所以约定上始终抛 Error 或它的子类。

catch 回调里又抛了错会怎样?

catch 返回的 Promise 同样会被拒绝,新的错误继续往下找下一个 catch。如果后面没有了,就是一个未处理的拒绝,而且原来那个错误的信息被新错误覆盖了,排查时要特别留意这种情况。

从一道题,走向一组知识

把知识连起来

异步与 Promise

Promise 的 finally 和 then 回调有什么不一样?

finally 回调里 throw 也会让链变成拒绝,而且会覆盖掉原来的成功值或错误,规则和 then 里 throw 一致。

异步与 Promise

new Promise 里传的函数是同步执行还是异步执行?

executor 里同步 throw 同样会变成拒绝,和 then 回调里 throw 是同一套捕获规则,只是发生在链的起点。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Using promises

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

本题目录
  1. 先记住这个答案
  2. 错误怎么沿链走下去
  3. 真实场景:一步出错,后面全部跳过
  4. 哪些错误接不住
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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