前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 catch 位置差异
JavaScript异步与 Promise

catch 放在 Promise 链中间和放在末尾,结果有什么不一样?

catch 放在中间,处理完错误后链会恢复成功,后面的 then 继续执行,拿到的是 catch 的返回值。放在末尾,前面任何一步出错都会跳过剩下的 then,直接落到这个 catch。

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

`catch` 也会返回一个新 Promise

  1. 中间的 catch局部兜底,处理完让链回到成功状态
  2. 末尾的 catch全局兜底,接住前面任何一步抛出的错误
  3. 重新 throw在 catch 里抛出,错误继续往下传给下一个 catch

catch 只能接住它前面的错误。末尾 catch 之后如果还接了 then,那个 then 里出的错就没人管了。

核心回答

先记住这个答案

catch 本质上是 then(undefined, onRejected),它和 then 一样会返回一个新的 Promise。放在中间时,它处理完错误就让链恢复成功,后面的 then 照常执行,拿到的是 catch 回调的返回值。放在末尾时,前面任何一步出错,中间剩下的 then 都会被跳过,错误直接落到这个 catch。所以中间的 catch 是局部兜底,末尾的 catch 是整条链的保险。

  • 中间的 catch 吃掉错误,后面的 then 照常执行
  • 末尾的 catch 兜住整条链的错误
  • catch 没有 return,后续拿到 undefined
  • 想继续失败,就在 catch 里重新 throw

两种位置的对比

同样是第一步就失败,只是 catch 的位置不同,后面那一步的命运完全不一样。

中间 catch 和末尾 catchJavaScript
function middle() {
  return Promise.reject(new Error('A 失败'))
    .catch((e) => console.log('中间 catch:', e.message)) // 没有 return,相当于返回 undefined
    .then((v) => console.log('B 照常执行,拿到', v)) // 链已经恢复成功
}

function tail() {
  return Promise.reject(new Error('A 失败'))
    .then(() => console.log('B 被跳过')) // 链处于失败状态,成功回调不执行
    .catch((e) => console.log('末尾 catch:', e.message))
}

middle().then(tail) // 先跑完 middle 再跑 tail,输出不交错
查看输出与解释
中间 catch: A 失败
B 照常执行,拿到 undefined
末尾 catch: A 失败

middle 里,catch 把错误处理掉了,而且回调正常返回,于是它返回的 Promise 变成成功状态,后面的 then 就执行了,拿到 undefined。 tail 里,B 那一步的 then 只注册了成功回调,链是失败状态,它就原样把错误往下传,一直传到末尾的 catch。 可以把 Promise 链想成一条流水线,失败就是一件次品在往下走。每个 then 只处理合格品,看到次品直接放行;catch 是质检员,把次品拦下来修好,放回流水线上继续走。质检员站在哪,决定了次品在哪一站被拦下。

catch 的返回值决定后面怎么走

catch 回调怎么结束,决定了它返回的那个 Promise 是什么状态,这是理解位置差异的关键。

三种结束方式
正常 return 一个值,比如 return [],链恢复成功,后面的 then 拿到这个值。这常用来给失败的请求一个默认数据,让页面照常渲染。 什么都不返回,后面拿到 undefined。上面例子就是这种情况,后面的 then 如果去读 v.length,就会抛出 TypeError,很多「错误处理了但页面还是报错」的 bug 就出在这里。 在 catch 里 throw,或者 return Promise.reject(err),链继续保持失败,错误交给下一个 catch。常见用法是中间的 catch 只负责上报日志,然后把错误重新抛出,让末尾的 catch 统一给用户提示。
回答前,多想一步

容易答错的地方

以为中间 catch 之后,后面的 then 就不执行了
正好相反。catch 正常返回后链就恢复成功了,后面的 then 一定会执行。如果不希望后续逻辑继续跑,要么在 catch 里重新 throw,要么把 catch 移到末尾。
以为 then 的第二个参数和后面接 catch 完全一样
p.then(onOk, onErr) 里,onErr 只能接住 p 的错误,接不住 onOk 自己抛出的错误。p.then(onOk).catch(onErr) 则两种错误都能接住,因为 catch 挂在 then 返回的新 Promise 上。
以为末尾有 catch 就万事大吉
catch 只管它前面的部分。如果 catch 后面又接了 then,或者 catch 回调自己抛了错,这些错误没有下一个 catch 接,就会变成未处理的拒绝,浏览器会触发 unhandledrejection 事件。
试着用自己的话回答

面试官还会怎么问?

链上有多个 catch,错误会被处理几次?

只会被第一个遇到的 catch 处理一次。第一个 catch 正常返回后链就恢复成功了,后面的 catch 不会执行。除非第一个 catch 重新 throw,错误才会继续传到下一个。

写成 async/await 时,catch 的位置对应什么?

对应 try/catch 包住的范围。只包一行 await,相当于中间的 catch,出错后函数会接着往下执行;把整个函数体都包进去,相当于末尾的 catch,任何一步出错都跳到 catch 块。

实际项目里中间 catch 一般用在哪?

用在「这一步失败了也能继续」的地方。比如页面同时要加载推荐列表,推荐接口挂了不应该让整页白屏,就在请求推荐的那一步后面接 catch(() => []),返回空数组,后面的渲染照常进行。

从一道题,走向一组知识

把知识连起来

异步与 Promise

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

finally 和 catch 一样能插在链的任意位置,但它不改变结果,原来的成功值或错误会原样穿过去。

异步与 Promise

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

链最开头的错误往往来自 executor,弄清 executor 什么时候执行,才知道最前面那个错误是怎么进链的。

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

参考资料

  • Promise.prototype.catch()

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

本题目录
  1. 先记住这个答案
  2. 两种位置的对比
  3. catch 的返回值决定后面怎么走
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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