前端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

前端阅读 05月30日 19:58

Chrome 如何管理内存?V8 垃圾回收和泄漏排查怎么做?

Chrome 的内存管理主要由多进程架构、Blink 渲染管线和 V8 垃圾回收一起完成。每个标签页、iframe、扩展或站点隔离进程都可能占用独立内存;JS 对象由 V8 管,DOM、样式、图片、GPU 资源则不完全在 JS 堆里。所以排查内存问题不能只看一个数字,要分清是 JS 堆增长、DOM 节点泄漏,还是图片、Canvas、WebGL 资源没有释放。追问V8 垃圾回收大概怎么工作?V8 会把对象分成新生代和老生代,短命对象优先在新生代快速回收,存活久的对象会晋升到老生代。最常见的内存泄漏有哪些?定时器没清、事件监听没移除、全局数组当缓存、闭包引用大对象、已删除 DOM 还被 JS 变量保存,都是高频原因。Chrome DevTools 怎么排查?先用 Performance 或 Task Manager 观察内存是否持续上升,再到 Memory 面板拍 Heap Snapshot,对比 Retained Size 和 Detached DOM tree。WeakMap 能解决所有泄漏吗?不能。它适合存跟对象生命周期绑定的附加信息,但如果外部还有全局引用,WeakMap 也救不了。代码里应该怎么清理?组件卸载时清理事件、定时器和订阅,长列表做虚拟滚动,大图和 Blob URL 用完释放。const timer = setInterval(sync, 5000);window.addEventListener('resize', onResize);return () => { clearInterval(timer); window.removeEventListener('resize', onResize); };
前端阅读 05月30日 19:58

Chrome Service Worker 是什么?生命周期和缓存坑有哪些?

Service Worker 是运行在页面之外的浏览器脚本,Chrome 用它来做请求拦截、离线缓存、推送通知和后台同步。它不直接操作 DOM,也不是一直常驻后台线程,而是被 install、activate、fetch、push 等事件唤醒,处理完任务后可能被浏览器回收。理解它的关键不是背 API,而是记住:它站在页面和网络之间,但生命周期由浏览器控制。追问Service Worker 和普通 JS 脚本有什么区别?普通 JS 跟着页面走,页面关闭脚本就结束;Service Worker 注册后独立于页面。它不能访问 window 和 DOM,需要用 postMessage、Cache Storage、IndexedDB 与页面协作。install、activate、fetch 分别做什么?install 适合预缓存静态资源,activate 适合清理旧版本缓存并接管客户端,fetch 决定请求走网络还是缓存。缓存策略怎么选?静态资源通常 cache first,HTML 入口常用 network first 或 stale-while-revalidate,接口数据按业务新鲜度决定。更新为什么经常“不生效”?Chrome 发现 sw.js 字节变化后会安装新版本,但旧页面仍被旧 worker 控制,新版本要等旧页面关闭才激活。必要代码长什么样?下面只是兜底示例,真实项目要区分静态资源、HTML 和接口。self.addEventListener('fetch', event => { event.respondWith(fetch(event.request).catch(() => caches.match(event.request)));});
前端阅读 05月30日 19:58

Chrome 如何支持 PWA?安装、离线和缓存边界是什么?

Chrome 支持 PWA,靠的不是某个“打包开关”,而是浏览器把 Web App Manifest、Service Worker、HTTPS、安装入口和权限能力串在一起。页面先要像普通网站一样可访问,再通过 manifest 告诉 Chrome 应用名称、图标、启动地址、显示模式和主题色;Service Worker 负责接管部分网络请求,让核心资源可以缓存,弱网或离线时仍有可用界面。追问Chrome 判断网站能不能安装成 PWA 看什么?通常要有 HTTPS、有效 manifest、合适尺寸图标,以及可被 Service Worker 控制的页面。安装提示还会结合用户访问行为,不是文件写好就立刻弹窗。Manifest 里最容易漏掉什么?常见漏项是 start_url、scope、display 和 192/512 尺寸图标。scope 配错会让用户从图标打开后跳回普通浏览器标签页。离线能力是不是把所有接口都缓存起来?不是。HTML 壳、CSS、JS、图标适合预缓存;用户数据、价格、库存、权限状态更适合网络优先或短缓存。更新 PWA 时有什么坑?Service Worker 更新不是刷新就立即生效,新 worker 要等旧页面关闭后才接管,除非主动调用 skipWaiting 和 clients.claim。最小实现需要哪些代码?页面注册 Service Worker,worker 在 install 阶段缓存核心资源,fetch 阶段兜底返回缓存。生产还要加版本号和清理旧缓存。if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js');}
前端阅读 05月30日 19:50

Chrome GPU 加速怎么实现?合成层、will-change 和动画怎么取舍?

Chrome 的 GPU 加速主要发生在合成阶段:页面先经过样式计算、布局、绘制,再把部分内容交给 GPU 作为合成层处理。它能让 transform、opacity 这类动画更顺,但不是所有属性都适合丢给 GPU;如果滥用,反而会增加显存、纹理上传和合成成本。追问哪些 CSS 更容易触发 GPU 合成?常见的是 transform、opacity,以及部分 filter、视频、Canvas、固定定位元素。开发里通常用 transform 做位移和缩放,不要用 left、top 频繁改位置。will-change 是不是越多越好?不是。它只是提前告诉浏览器这个元素可能要变,浏览器可能因此提前建层和分配资源。动画结束后应移除。GPU 加速为什么有时反而变慢?合成层不是免费的,层太多会占显存,内容变化频繁还会导致纹理反复上传。低端设备最容易出问题。怎么确认页面真的用了合成层?用 DevTools 的 Layers、Performance 和 Rendering 工具。看是否有大量 Layout、Paint,或异常多图层。写段 CSS.card { will-change: transform, opacity; }.card.is-moving { transform: translate3d(0, 12px, 0); opacity: .92; }
前端阅读 05月30日 19:50

Chrome DevTools 调试技巧有哪些?断点、网络和性能怎么用?

Chrome 调试的核心不是把 DevTools 每个面板都点一遍,而是按问题类型选工具:样式错看 Elements,请求慢看 Network,逻辑异常看 Sources,卡顿掉帧看 Performance,内存上涨看 Memory。这样排查更快,也不容易在 Console 里靠猜。追问Elements 面板适合解决什么问题?它适合查 DOM 结构、CSS 覆盖关系、盒模型尺寸和最终计算样式。要注意样式可能来自媒体查询、伪类、继承和组件库默认规则。Sources 断点怎么用才高效?普通断点查确定位置,条件断点只在某个参数或状态出现时暂停,XHR/fetch 断点适合追接口是谁发起的。Network 面板看哪些字段?先看 Status、Method、Initiator、Payload、Response,再看 Timing 里的 DNS、SSL、TTFB 和下载耗时。性能卡顿从哪里开始录?用 Performance 面板录制真实操作,重点看 Main 线程长任务、Layout、Recalculate Style 和 Paint。
前端阅读 05月30日 19:50

Chrome 隐私保护功能有哪些?无痕、Cookie 和权限怎么设置?

Chrome 的隐私保护不是只开无痕窗口就够了。无痕模式只能让本机不保留历史记录、Cookie 和表单数据,网站、公司网络、运营商仍然可能看到访问行为。真正有用的做法,是把 Cookie、网站权限、安全浏览、密码检查、同步数据和扩展权限一起管起来。追问无痕模式到底保护什么?它主要保护这台设备上的痕迹,比如浏览历史、临时 Cookie、搜索记录和表单输入。它不等于匿名上网。第三方 Cookie 要不要全部禁用?多数情况下建议阻止第三方 Cookie,因为它们常用于跨站跟踪。遇到老旧登录或支付异常时,可以只给可信站点加例外。哪些权限最容易被忽略?位置、摄像头、麦克风、通知、剪贴板和后台同步最容易被随手允许。通知权限尤其要定期清理。扩展程序会不会破坏隐私设置?会,尤其是申请“读取和更改所有网站数据”的扩展。安装前看权限,安装后清理不用的扩展。
前端阅读 05月30日 19:50

Astro View Transitions 如何工作?怎么避免页面过渡踩坑?

Astro View Transitions 的核心是:页面仍然按多页应用导航,但 Astro 在跳转时拦截链接、获取新页面、替换 DOM,并借助浏览器 View Transitions API 做过渡动画。它给静态站带来接近 SPA 的切页手感,但不等于把站点变成完整 SPA;SEO、静态输出和按页加载的优势仍保留。追问View Transitions 和 SPA 路由有什么区别?SPA 通常只替换客户端状态和组件树。Astro 更像增强版 MPA 导航,新页面仍是单独文档,只是在切换时接管加载和替换过程。为什么脚本状态会丢?导航后 DOM 会被替换,旧节点上的事件和第三方实例可能不存在。需要在 astro:page-load 里重新初始化必要脚本。transition:name 有什么边界?同一次过渡里名称应当唯一。列表页要用 slug 或 id 拼接名称,不要所有卡片都叫 cover。什么时候不该使用页面过渡?表单提交、支付、登录回调、后台管理不适合花哨动画。内容站、作品集、相册更适合,但也要控制范围。写段代码<ViewTransitions /><img src={post.cover} transition:name={`cover-${post.slug}`} />
前端阅读 05月30日 19:50

Astro 如何做好 SEO?Meta、结构化数据和站点地图怎么配?

Astro 做 SEO 的优势不在插件多,而在默认输出更接近搜索引擎喜欢的页面:静态 HTML、少 JavaScript、首屏快、内容能直接被爬虫读到。落地时重点不是堆满 meta,而是保证每页有唯一标题、稳定 canonical、准确 description、可分享 OG、合适结构化数据,以及能被发现的 sitemap。追问Astro 为什么比很多 SPA 更适合 SEO?Astro 默认把页面渲染成 HTML,爬虫不需要等客户端 JavaScript 执行完才看到正文。但如果正文依赖客户端接口再拉取,仍可能被漏抓。Meta 标签是不是越多越好?不是,title、description、canonical、OG 和必要的 Twitter Card 通常就够了。每页唯一、准确、和正文匹配更重要。sitemap 和 robots.txt 有哪些坑?最常见的是 site 没配,导致 sitemap 生成错误域名;robots.txt 不提升排名,只控制抓取边界。SEO 组件放 layout 还是页面?基础标签可以放 layout,但标题、描述、发布时间、文章类型必须由页面传入,避免大量页面共用同一 description。写段代码<title>{title}</title><meta name="description" content={description} /><link rel="canonical" href={url} /><script type="application/ld+json" set:html={JSON.stringify(schema)} />
前端阅读 05月30日 19:40

Expo 框架适合什么类型的 React Native 项目?

Expo 是建立在 React Native 之上的框架和工具链,它把创建项目、真机预览、常用原生能力、路由、云端构建、商店提交和 OTA 更新放到一套工作流里。它适合快速验证产品、跨平台业务 App、前端团队主导的移动端项目,以及希望少碰 Xcode、Gradle、证书和原生桥接的团队。追问Expo 和 React Native 是什么关系?React Native 是底层跨平台运行框架,负责把 React 组件渲染成原生 UI;Expo 是围绕 React Native 的 SDK、CLI、路由和云服务。Expo Go、Development Build、EAS 分别做什么?Expo Go 适合快速预览通用能力;Development Build 是带有你自己原生配置的调试包;EAS 负责云端构建、提交商店和发布更新。哪些项目特别适合用 Expo?内容型 App、工具型 App、内部系统、轻量电商、社区产品、MVP 验证都很适合,因为它们更看重交付速度和跨平台一致性。什么场景不适合只依赖 Expo Go?涉及自定义原生模块、厂商 SDK、推送、深链、后台任务、权限文案或原生启动页时,应尽早切到 development build。Expo 的主要取舍是什么?收益是上手快、常用能力齐全、构建发布链路顺;代价是要遵守 Expo 的版本节奏和配置方式。写段命令npx create-expo-app my-appnpx expo startnpx expo run:ios
前端阅读 05月30日 19:40

Expo 常用 API 如何在项目里安全使用?

Expo 常用 API 是 Expo SDK 对原生能力的封装,常见场景包括相机、定位、通知、文件系统、媒体库、传感器、安全存储和剪贴板。它的好处是用 JavaScript/TypeScript 调 iOS、Android 能力,少写原生桥接;安全使用的重点是权限、平台差异、开发构建方式和失败兜底是否处理完整。追问Expo 常用 API 可以分成哪些类型?设备能力类包括 Camera、Location、Notifications、Sensors;系统能力类包括 FileSystem、SecureStore、Clipboard;媒体类包括 ImagePicker、Audio、Video。为什么权限明明申请了还是拿不到?很多权限既要运行时申请,也要在 app.json 或原生配置里声明用途文案。iOS 用户拒绝后再次请求不一定弹窗,Android 不同版本也有差异。Expo API 和自定义原生模块怎么取舍?常规能力优先用 Expo SDK;需要厂商支付、蓝牙深度能力、特殊后台服务时,要考虑 development build、config plugin 或原生模块。Expo Go 能不能验证所有 API?不能。Expo Go 适合预览通用能力,但不能包含你的自定义原生配置,也不适合验证真实推送证书、深链、后台任务。实战里权限请求应该放在哪里?不要每次渲染都请求权限,也不要一进 App 就弹所有权限。更稳的是在用户触发相机、定位、上传图片等动作时再申请。写段代码const { status } = await Location.requestForegroundPermissionsAsync();if (status === 'granted') await Location.getCurrentPositionAsync({});
前端阅读 05月30日 19:40

Expo EAS Build、Submit 和 Update 分别解决什么问题?

Expo EAS 是 Expo Application Services,主要把移动应用从“本地能跑”推进到“团队可构建、可提交、可更新”。Build 负责在云端产出 iOS/Android 安装包,Submit 负责把产物提交到 App Store 或 Google Play,Update 负责在不改原生二进制的前提下推送 JS 和资源更新。追问EAS Build 解决的核心问题是什么?它把 Xcode、Gradle、证书、签名、构建机环境这些麻烦事集中到云端处理。团队成员不必每个人都配置一套原生打包环境。EAS Submit 和 EAS Build 有什么区别?Build 只负责生成 ipa、aab 或 apk,Submit 负责把产物交给应用商店。构建成功不代表一定能提交成功,商店账号、版本号和证书仍要匹配。EAS Update 能更新所有线上问题吗?不能。它只能更新 JavaScript、图片、字体等资源,不能新增原生模块、修改权限、scheme 或 Android 原生配置。eas.json 的 profile 应该怎么设计?development 用 developmentClient,preview 用于内测分发,production 放商店发布配置。不要让测试包和生产包共用同一条 update channel。团队使用 EAS 最容易踩什么坑?Expo Go 能跑不代表生产包没问题;OTA 更新也要记录 runtimeVersion、channel 和提交哈希,出问题才能回滚。写段配置{"build":{"development":{"developmentClient":true},"preview":{"distribution":"internal"},"production":{"autoIncrement":true}}}
前端阅读 05月30日 19:40

Expo Router 文件系统路由是如何工作的?

Expo Router 是 Expo 官方推荐的路由方案,它把 app 目录里的文件映射成页面路径:app/user/[id].tsx 对应 /user/:id,_layout.tsx 负责给同一层页面套 Stack、Tabs、Modal 或全局 Provider。它底层仍使用 React Navigation,不是另起炉灶,而是把手写 screen 配置改成文件约定。追问Expo Router 和 React Navigation 有什么关系?Expo Router 底层依然依赖 React Navigation,导航能力并没有少。区别是通过文件结构生成路由,适合页面多、需要深链或同时支持 Web 的 Expo 项目。文件系统路由是怎么工作的?app/index.tsx 是首页,app/settings.tsx 是 /settings,app/user/[id].tsx 是动态路由。(tabs) 目录用于分组,不直接出现在 URL 里。动态参数应该怎么读取?常用 useLocalSearchParams 读取当前页面参数。参数通常是字符串或字符串数组,接口需要数字时要自己转换和校验。深度链接和 Web URL 有哪些坑?改 scheme、Universal Links、Android App Links 后,通常要重新打 Development Build 或生产包验证。Expo Go 能跳不代表商店包配置正确。项目变大后怎么组织路由?把认证、Tabs、弹窗、业务模块分到不同分组里,让每个 _layout.tsx 只管当前层级的导航。写段代码const { id } = useLocalSearchParams<{ id: string }>();router.push(`/user/${id}/orders`);
前端阅读 05月30日 19:40

Expo Web 如何实现跨端开发并避开常见坑?

Expo 支持 Web 的基础是 React Native for Web:View、Text、Pressable 等组件会映射到浏览器 DOM,再配合 Metro、Expo Router 和静态导出,把同一套业务代码跑到 iOS、Android 和 Web。它最大的价值是复用,而不是把移动端应用一键变成高质量网站。上线时,Web 端还要单独处理 SEO、响应式宽度、鼠标键盘交互、无障碍和浏览器 API 差异。追问Expo Web 怎么启动和发布?开发时用 npx expo start --web。发布静态站点常用 npx expo export --platform web,再把产物交给 Vercel、Netlify、Nginx 或对象存储。React Native for Web 做了什么?它把 React Native 组件和样式模型翻译成 Web 可理解的结构。好处是组件复用,代价是并非所有原生能力都有 Web 等价物。哪些代码需要平台区分?相机、推送、文件系统、安全存储、分享、下载和路由跳转最容易分叉。简单差异用 Platform.OS,差异大时用 .web.tsx。跨端样式最容易踩什么坑?移动端固定宽度、全屏弹层、触摸优先交互放到桌面会很别扭。Web 端要补 hover、focus、键盘操作和宽屏断点。SEO 和 PWA 能自动做好吗?不能。Expo Router 能让 URL 更自然,但标题、描述、结构化内容、静态导出策略、manifest 和缓存策略仍要自己设计。写段代码const padding = Platform.select({ web: 24, default: 16 });
前端阅读 05月30日 19:40

Expo 开发应该用哪些调试工具排查问题?

Expo 调试工具可以按问题类型来选:启动失败先看 Expo CLI 和 Metro 日志,页面状态异常用 React Native DevTools,真机连接问题看设备日志和网络环境,性能问题再上 Profiler。现在不建议把老的“远程 JS 调试”当默认方案,因为 Hermes、New Architecture 和新版 React Native DevTools 已经改变了调试路径。追问Expo CLI 在调试里主要做什么?Expo CLI 负责启动 Metro、生成二维码、打开模拟器、切换连接方式和清缓存。常用 npx expo start,遇到 bundle 或资源缓存异常,先试 npx expo start -c。React Native DevTools 怎么打开?项目跑起来后,在终端按 j 通常就能打开。它可以看 Console、Sources、Network、Components 和 Profiler,比旧工具更贴近 Hermes 调试体验。Expo Go 和 Development Build 调试有什么区别?Expo Go 适合验证纯 JS 和官方内置模块,启动快,但不能覆盖所有自定义原生能力。加了 config plugin、推送、深链或第三方 SDK,就应用 Development Build。真机连不上开发服务器怎么排查?先确认手机和电脑在同一网络,防火墙、代理、VPN、公司 Wi-Fi 隔离都可能导致失败。LAN 不行可以临时用 tunnel,但不适合判断性能。性能问题应该看哪个工具?先用 Profiler 看组件是否重复渲染,再看 Network 是否重复请求或接口太慢。滚动、动画、输入卡顿一定要在真机或接近生产的 Development Build 里测。写段代码if (__DEV__) { console.group('user'); console.log(user.id, user.role); console.groupEnd(); }debugger;
前端阅读 05月30日 19:40

Expo 动画该用 Animated、Reanimated 还是 Lottie?

Expo 里做动画,先看动画的“控制权”在哪里:只是按钮淡入、卡片位移、骨架屏闪一下,用 React Native 自带 Animated 就够;动画要跟手势实时绑定、拖拽时不能掉帧,优先用 React Native Reanimated;如果设计师已经从 After Effects 导出了复杂插画,就用 Lottie。别把三者理解成替代关系,它们更像三种入口:Animated 轻、Reanimated 稳、Lottie 还原设计稿快。追问Animated 和 Reanimated 到底差在哪?Animated 适合透明度、缩放、平移这类常规过渡,API 学习成本低。Reanimated 的共享值和 worklet 能跑在 UI 线程,手势拖拽、底部抽屉、列表联动更稳,但写法和调试成本也更高。Lottie 适合替代手写动画吗?不适合全部替代。Lottie 很适合加载、空状态、品牌动效,因为它能最大程度还原设计资源;但它不适合承载复杂业务状态。Expo 项目里需要额外配置什么?依赖建议用 npx expo install react-native-reanimated lottie-react-native,版本会跟当前 SDK 对齐。Reanimated 还要确认 Babel 插件放在 plugins 最后。动画卡顿时先排查哪里?先看动画是不是被 JS 线程阻塞,比如大量 setState、日志、JSON 解析或列表重渲染。能用 transform 就别改布局尺寸。实战选型有什么取舍?简单动效别强行上 Reanimated,后续维护成本更高;设计师交付 Lottie JSON 时优先 Lottie;和手势强绑定再用 Reanimated。写段代码const x = useSharedValue(0);const style = useAnimatedStyle(() => ({ transform: [{ translateX: withSpring(x.value) }] }));
前端阅读 05月30日 15:19

Expo 应用如何实现可访问性?有哪些最佳实践?

Expo 的可访问性主要靠 React Native 的 accessibility* 属性实现:给可交互元素补 label、role、hint,用 state/value 表达状态和值,动态变化用 AccessibilityInfo 或 liveRegion 通知读屏,并在 iOS VoiceOver、Android TalkBack 上真机测试。优先保证按钮、图片、表单、弹窗、列表项能被读屏准确读出、能被键盘或辅助触控操作,别只靠颜色传达信息。追问accessibilityLabel、Hint、Role 分别做什么?label 说明“这是什么”,role 说明“它是什么控件”,hint 说明“触发后会发生什么”。状态和值怎么告诉屏幕阅读器?复选框、开关用 accessibilityState;滑块、进度条用 accessibilityValue,提供 min、max、now。实际项目怎么验收?iOS 开 VoiceOver、Android 开 TalkBack,按完整流程走一遍:登录、表单报错、列表操作、弹窗关闭、深色/高对比、减少动画都要测。写段代码<Pressable accessibilityRole="button" accessibilityLabel="删除订单" accessibilityHint="删除后无法恢复"> <Text>删除</Text></Pressable>
前端阅读 05月30日 10:11

Prettier 支持哪些语言和文件类型?

Prettier 原生支持 JavaScript、TypeScript、JSX、TSX、CSS、SCSS、Less、HTML、Vue、Angular、JSON、YAML、Markdown、MDX、GraphQL 等常见前端和文档格式。Java、PHP、Ruby、XML 等也可以通过插件或社区解析器支持。判断能不能格式化,关键看 Prettier 是否有对应 parser。追问Prettier 原生支持哪些文件?常见有 .js、.jsx、.ts、.tsx、.css、.scss、.less、.html、.vue、.json、.yaml、.md、.mdx、.graphql。插件支持和原生支持有什么区别?原生支持开箱即用;插件支持需要额外安装包,团队里要统一依赖版本,否则 CI 和本地结果可能不一致。Prettier 怎么判断用哪个解析器?通常根据扩展名自动选择 parser。识别不了时,可以用 overrides 指定 parser。写段代码{"overrides":[{"files":"*.vue","options":{"parser":"vue"}}]}
前端阅读 05月30日 10:11

Promise.all 和 Promise.race 有什么区别?

Promise.all 等“全部成功”,Promise.race 等“第一个完成”。all 会并行启动所有 Promise,只有全部 fulfilled 才 fulfilled,结果数组顺序和传入顺序一致;任意一个 rejected,整体立刻 rejected。race 也是并行启动,但谁先 settled 就采用谁的结果,不管成功还是失败。追问all 和 race 的返回值有什么不同?all 返回结果数组;race 返回第一个完成的值或错误。all 里有一个失败,其他请求会取消吗?不会。all 只是让返回的 Promise 变成 rejected,已经发出去的请求仍会继续,除非额外用 AbortController 取消。空数组会怎样?Promise.all([]) 立即 fulfilled,值是 [];Promise.race([]) 会一直 pending。allSettled 和 any 什么时候用?想知道每个任务成败,用 allSettled;只要任意一个成功就够,用 any。写段代码const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 3000));Promise.race([fetch('/api/data'), timeout]).catch(console.error);Promise.all([fetch('/a'), fetch('/b')]);
前端阅读 05月30日 10:11

Promise 链式调用是怎么工作的?

Promise 链式调用的核心是:每次调用 .then() 都会返回一个新的 Promise,后一个 .then() 接收前一个回调的返回值。返回普通值就直接传下去;返回 Promise 就等待它 settled;抛错或返回 rejected Promise,错误会沿链向后冒泡,直到被 .catch() 捕获。追问then 里返回普通值和 Promise 有什么区别?返回普通值时,下一个 then 立即拿到这个值;返回 Promise 时,下一个 then 要等它完成后再执行。then 里不 return 会怎样?等于返回 undefined,所以下一个 then 收到的就是 undefined。很多链式调用断数据,问题都出在这里。catch 后面的 then 还会执行吗?会。catch 如果返回正常值,链会恢复为 fulfilled;如果继续 throw,后面仍然走 rejected 分支。async/await 和链式调用是什么关系?async/await 本质是 Promise 的语法糖,适合写顺序流程;链式调用适合短管道或函数组合。写段代码Promise.resolve(1) .then(v => v + 1) .then(v => Promise.resolve(v * 2)) .then(v => { throw new Error('bad') }) .catch(() => 'fallback') .then(console.log);
前端阅读 05月30日 02:24

Promise 错误处理面试怎么答?

Promise 错误处理要抓住两句话:错误会沿 Promise 链向后传播,最近的 catch 或 then 第二个参数会接住它;catch 返回普通值表示恢复,重新 throw 才会继续失败。项目里一般推荐在链尾统一 catch,async/await 用 try/catch;多个任务用 Promise.allSettled 处理部分失败,避免一个请求失败拖垮全部结果。追问catch 和 then 的第二个参数有什么区别?then 的第二个参数只能处理前一个 Promise 的失败,抓不到同一个 then 成功回调里新抛出的错误;catch 更适合放在链尾统一兜底。catch 里 return 和 throw 有什么区别?return 会把链恢复成 fulfilled,后面的 then 会继续走;throw 或 return rejected Promise 才会让后续 catch 接着处理。Promise.all 里一个失败怎么办?Promise.all 会快速失败,只要一个 reject 整体就 reject。需要拿到每个任务结果时,用 Promise.allSettled,或给每个任务单独 catch。未捕获的 Promise 错误怎么排查?浏览器看 unhandledrejection,Node 看 unhandledRejection 日志。根因通常是忘记 await、忘记 return Promise,或 catch 里吞错。写段代码async function loadAll(tasks) { const results = await Promise.allSettled(tasks.map(t => t())); return results.map(r => r.status === 'fulfilled' ? r.value : { error: r.reason.message } );}