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

什么时候会触发 unhandledrejection?怎么避免未处理的 Promise 拒绝?

一个 Promise 被拒绝,而且在当前微任务队列清空时还没有挂上 catch 或 then 的第二个参数,就会触发 unhandledrejection。避免的办法是每条 Promise 链的末尾都有人处理错误,async 函数的调用要么 await 要么接 catch。

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

一个拒绝从产生到被报出来

  1. 拒绝reject 被调用,或者 then 回调、async 函数里抛了错
  2. 处理函数catch、then 的第二个参数、await 外层的 try/catch
  3. unhandledrejection微任务清空后仍没有处理函数,浏览器在 window 上派发这个事件

处理函数晚一点挂上也算数,只是那时 unhandledrejection 已经发出去了,浏览器会再补发一个 rejectionhandled 事件。

核心回答

先记住这个答案

当一个 Promise 变成 rejected,并且在当前这轮微任务全部执行完时,它身上还没有任何拒绝处理函数,浏览器就会在 window 上触发 unhandledrejection 事件,控制台里也会出现 Uncaught (in promise) 的报错。最常见的原因是 Promise 链末尾没有 catch、catch 挂在了错误的那个 Promise 上,或者调用 async 函数时既没有 await 也没有接 catch。避免的原则只有一条:每一条链的最后都要有人处理错误。

  • 被拒绝且没有处理函数时触发,检查时机是微任务清空后
  • then 会返回新 Promise,catch 要挂在链的末尾
  • 调用 async 函数不 await 也不 catch,报错就没人接
  • 全局监听只适合上报,不能代替正常的错误处理

最常见的三种漏处理写法

「处理」指的是给这个 Promise 挂上拒绝回调,包括 catch、then 的第二个参数,或者在 try 里 await 它。下面这些写法都会触发事件。

全局监听和几种触发场景JavaScript
window.addEventListener('unhandledrejection', (event) => {
  console.log('未处理的拒绝', event.reason)
  // event.preventDefault() 可以阻止控制台打印默认报错
})

Promise.reject(new Error('没人接')) // 1. 直接拒绝,没有 catch

async function save() { throw new Error('保存失败') }
save() // 2. async 函数抛错变成被拒绝的 Promise,调用方没 await

Promise.resolve().then(() => { throw new Error('回调里抛错') }) // 3. then 返回的新 Promise 没人处理
// 三条都会触发 unhandledrejection

第二种最容易漏。async 函数里 throw 不会像普通函数那样直接抛到调用方,而是让它返回的 Promise 变成 rejected。调用方如果只是 save() 一下,外面包再多层 try/catch 也接不住,因为 try 块早就同步执行完了。

catch 挂错位置

then 每调用一次都会返回一个新的 Promise。原 Promise 失败时,这个新 Promise 也会跟着失败,而它是一个独立的对象,需要单独处理。

分开写的 then 和 catchJavaScript
const p = request()

p.then(render) // 返回新 Promise,request 失败时它也失败
p.catch(showError) // 只处理了 p 本身

// request 失败时:showError 会执行,但 p.then(render) 那条链没人处理,照样触发
// 正确写法:request().then(render).catch(showError)

这种写法看起来有 catch,实际上有两条链,只处理了一条。把 catch 接在整条链的最后,它就能同时接住 request 的失败和 render 里抛出的错误。

晚一点挂处理函数会怎样
浏览器是在微任务队列清空后检查的。如果先创建了一个被拒绝的 Promise,过了一个 setTimeout 才去 catch,那么 unhandledrejection 已经发出去了。等 catch 挂上后,浏览器会再派发一个 rejectionhandled 事件。监控系统如果只听前一个,就会多报一条其实被处理了的错误。
回答前,多想一步

容易答错的地方

以为外层 try/catch 能接住所有 Promise 错误
try/catch 只能接住同步抛出的错误,以及在 try 里被 await 的 Promise 的拒绝。没有 await 的 Promise 是在 try 块结束后才失败的,这时已经出了 try 的范围。
以为 Promise.all 中其它失败的项也会报未处理
Promise.all 会给每一项都挂上处理函数。第一个失败决定了结果,后面再失败的项已经被 all 内部处理过了,不会再触发事件。只要 all 返回的那个 Promise 有 catch 就够了。
把全局监听当成错误处理
unhandledrejection 适合做兜底上报,比如把 event.reason 发给 Sentry。它拿不到业务上下文,没法给用户正确的提示或者回滚状态。正常的错误处理还是要写在每个调用点上。
试着用自己的话回答

面试官还会怎么问?

Node.js 里未处理的拒绝会怎样?

Node.js 从 15 版本开始,默认行为是把未处理的拒绝当成未捕获异常,直接让进程退出。可以用 process.on('unhandledRejection', handler) 监听做日志,但更稳妥的做法是修掉漏处理的地方,而不是靠监听让进程活下来。

forEach 里写 async 回调,为什么错误接不住?

forEach 不会等回调返回的 Promise,也不会处理它们的拒绝。每个 async 回调失败时都是一个没人管的 Promise,外面 await 什么都等不到。要么改用 for...of 加 await,要么用 map 收集 Promise 再交给 Promise.all 统一处理。

故意不关心结果的 Promise,怎么避免报错?

明确地接一个空处理,比如 sendLog().catch(() => {}),同时最好在里面打一条日志。这样读代码的人能看出这是有意忽略,而不是忘了处理。

从一道题,走向一组知识

把知识连起来

异步与 Promise

递归处理层级不确定的异步任务,怎么避免爆栈和 Promise 链越来越长?

递归的 Promise 链中间任何一环失败,都要保证错误能一路传到最外层被处理。

异步与 Promise

一组返回 Promise 的函数,怎么用 reduce 串行执行并把上一个结果传给下一个?

用 reduce 串行执行时只需在最终 Promise 上接一个 catch,正好练习错误怎么沿链传递。

参考资料

  • JavaScript:异步与 Promise参考资料
  • Window:unhandledrejection 事件

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

本题目录
  1. 先记住这个答案
  2. 最常见的三种漏处理写法
  3. catch 挂错位置
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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