别再死记硬背Promise面试题了!我用这5个真实场景帮你彻底搞懂.then和.catch
2026/5/4 21:50:37 网站建设 项目流程

用5个真实开发场景彻底掌握Promise的.then与.catch

在面试中遇到Promise相关的题目时,很多开发者会感到头疼——不是因为他们不理解Promise的基本概念,而是因为实际开发中的异步场景远比面试题复杂得多。本文将带你跳出死记硬背的怪圈,通过5个真实开发场景,深入理解.then和.catch的运作机制。

1. 用户登录流程中的状态管理

登录流程是前端开发中最常见的异步操作之一。想象一个典型的登录场景:用户输入凭证后,我们需要验证输入格式、发送登录请求、处理响应、更新用户状态,最后跳转页面。这个过程中每一步都可能出错,如何优雅地处理这些异步操作?

function handleLogin(credentials) { return validateInput(credentials) .then(() => authenticateUser(credentials)) .then(user => { store.dispatch(loginSuccess(user)); return fetchUserProfile(user.id); }) .then(profile => { store.dispatch(updateProfile(profile)); router.push('/dashboard'); }) .catch(error => { if (error instanceof ValidationError) { showToast('请输入有效的邮箱和密码'); } else if (error instanceof AuthError) { showToast('用户名或密码错误'); } else { showToast('登录失败,请稍后重试'); trackError(error); } }); }

关键点解析:

  • 链式调用让异步流程更清晰
  • 每个.then处理前一步的成功结果
  • 单个.catch捕获整个链条中的任何错误
  • 根据错误类型提供不同的用户反馈

2. 并行API请求的成功/失败聚合

当我们需要同时发起多个独立API请求并等待所有完成时,Promise.all是常用选择。但它的"全有或全无"特性有时并不理想——一个请求失败会导致整个Promise被拒绝。如何实现更灵活的并行请求处理?

async function fetchDashboardData() { const [userReq, ordersReq, messagesReq] = await Promise.allSettled([ fetch('/api/user'), fetch('/api/orders'), fetch('/api/messages') ]); return { user: userReq.status === 'fulfilled' ? userReq.value : null, orders: ordersReq.status === 'fulfilled' ? ordersReq.value : [], messages: messagesReq.status === 'fulfilled' ? messagesReq.value : [], errors: [userReq, ordersReq, messagesReq] .filter(req => req.status === 'rejected') .map(req => req.reason) }; }

对比Promise.all与Promise.allSettled:

特性Promise.allPromise.allSettled
全部成功时的返回值结果数组结果数组
有失败时的行为立即拒绝等待所有完成
错误处理方式只能捕获第一个错误可获取所有状态
适用场景强依赖所有结果部分结果可接受

3. 带自动重试机制的图片懒加载

图片懒加载是提升页面性能的常用技术,但网络不稳定可能导致加载失败。实现一个带指数退避重试机制的懒加载组件,能显著提升用户体验。

function loadImageWithRetry(url, retries = 3, delay = 1000) { return new Promise((resolve, reject) => { const attempt = (remainingRetries) => { loadImage(url) .then(resolve) .catch(error => { if (remainingRetries <= 0) { reject(error); return; } setTimeout(() => { attempt(remainingRetries - 1); }, delay * (retries - remainingRetries + 1)); }); }; attempt(retries); }); } // 使用示例 loadImageWithRetry('https://example.com/image.jpg') .then(img => { document.getElementById('lazy-image').src = img.url; }) .catch(() => { document.getElementById('lazy-image').src = '/placeholder.jpg'; });

重试策略优化建议:

  • 初始延迟时间不宜过长(建议500ms-1s)
  • 每次重试增加延迟时间(指数退避)
  • 设置最大重试次数(通常3-5次)
  • 最终失败时提供优雅降级方案

4. 构建异步任务队列确保顺序执行

某些场景下我们需要确保一系列异步函数按顺序执行,比如文件上传、数据库迁移等。实现一个任务队列可以优雅地解决这个问题。

class AsyncQueue { constructor() { this.queue = []; this.isProcessing = false; } enqueue(task) { return new Promise((resolve, reject) => { this.queue.push({ task, resolve, reject }); if (!this.isProcessing) this.processQueue(); }); } async processQueue() { this.isProcessing = true; while (this.queue.length > 0) { const { task, resolve, reject } = this.queue.shift(); try { const result = await task(); resolve(result); } catch (error) { reject(error); } } this.isProcessing = false; } } // 使用示例 const queue = new AsyncQueue(); queue.enqueue(() => fetch('/api/step1')); queue.enqueue(() => fetch('/api/step2')); queue.enqueue(() => fetch('/api/step3'));

任务队列的核心优势:

  • 保证任务按添加顺序执行
  • 自动处理异步操作间的依赖
  • 统一的错误处理机制
  • 易于扩展和复用

5. React/Vue组件中的异步副作用处理

在现代前端框架中,组件常需要处理异步副作用,如数据获取、事件订阅等。如何在这些场景中合理使用Promise,避免常见陷阱?

React示例(使用Hooks):

function UserProfile({ userId }) { const [user, setUser] = useState(null); const [error, setError] = useState(null); useEffect(() => { let isMounted = true; fetchUser(userId) .then(data => { if (isMounted) setUser(data); }) .catch(err => { if (isMounted) setError(err.message); }); return () => { isMounted = false; }; }, [userId]); if (error) return <Error message={error} />; if (!user) return <Loading />; return <ProfileCard user={user} />; }

Vue示例(使用Composition API):

import { ref, onMounted, onUnmounted } from 'vue'; export default { setup(props) { const user = ref(null); const error = ref(null); let isMounted = true; onMounted(() => { fetchUser(props.userId) .then(data => { if (isMounted) user.value = data; }) .catch(err => { if (isMounted) error.value = err.message; }); }); onUnmounted(() => { isMounted = false; }); return { user, error }; } };

组件中处理异步的注意事项:

  • 组件卸载时取消未完成的异步操作
  • 使用状态标记避免"设置已卸载组件"的警告
  • 提供加载中和错误状态UI
  • 依赖项变化时重新获取数据
  • 考虑使用防抖/节流优化频繁请求

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询