如何阻止Promise传播而非拒绝?SPA会话过期重定向后终止后续执行
解决方案
针对你在Vue+Nuxt.js SPA中遇到的会话过期重定向后仍执行后续代码的问题,以下两种方案可以满足需求,无需在每个业务服务中重复处理会话过期逻辑:
方案一:全局拦截会话过期错误,阻止错误提示触发
通过自定义错误类型结合全局拦截机制,让会话过期错误不触发默认的错误提示条,同时完成登录页跳转。
1. 定义自定义错误类型
创建独立的错误类用于标识会话过期:
// utils/errors.js export class SessionExpiredError extends Error { constructor(message) { super(message); this.name = 'SessionExpiredError'; } }
2. 改造HttpClient的request方法
检测到会话过期时,先执行登录页跳转,再抛出自定义错误:
import { SessionExpiredError } from '@/utils/errors'; import { navigateTo } from '#app'; export default class HttpClient { // ... 其他代码 private async redirectToLogin() { // 清除本地会话凭证 localStorage.removeItem('authToken'); // 使用Nuxt的SPA导航跳转,避免页面刷新 await navigateTo('/login'); } public async request(params) { // ... 原有参数处理逻辑 try { const response: ApiResponse = await $fetch(fetchUrl.toString(), requestOptions); if (this.hasError(response)) { const errorMessage = this.getErrorMessage(response as ErrorApiResponse); if (await this.checkSessionExpired(errorMessage)) { await this.redirectToLogin(); return Promise.reject(new SessionExpiredError(errorMessage)); } return Promise.reject(new ApiHandledError(errorMessage)); } return this.getData(response as SuccessApiResponse); } catch (error) { // 捕获请求层面的401等会话过期错误 if (await this.checkSessionExpired(error.message || error.statusText)) { await this.redirectToLogin(); return Promise.reject(new SessionExpiredError('会话已过期')); } return Promise.reject(error); } } }
3. 全局拦截错误,阻止提示触发
在Nuxt插件中添加全局错误拦截,忽略会话过期错误:
// plugins/error-handler.js import { SessionExpiredError } from '@/utils/errors'; export default defineNuxtPlugin((nuxtApp) => { // 拦截全局未处理的Promise拒绝 window.addEventListener('unhandledrejection', (event) => { if (event.reason instanceof SessionExpiredError) { // 阻止默认错误处理(如错误提示条) event.preventDefault(); } }); // 拦截Nuxt应用级错误 nuxtApp.hook('app:error', (error) => { if (error instanceof SessionExpiredError) { // 返回true阻止错误传播,不显示默认错误页面 return true; } }); });
方案二:返回永久Pending的Promise,终止后续代码执行
当检测到会话过期时,直接返回一个永远处于pending状态的Promise,调用方的.then()/.catch()逻辑都不会执行(注意:.finally()仍会执行,若有清理逻辑需额外处理)。
改造request方法:
public async request(params) { // ... 原有代码 try { const response: ApiResponse = await $fetch(fetchUrl.toString(), requestOptions); if (this.hasError(response)) { const errorMessage = this.getErrorMessage(response as ErrorApiResponse); if (await this.checkSessionExpired(errorMessage)) { await this.redirectToLogin(); // 返回永久pending的Promise,终止后续代码 return new Promise(() => {}); } return Promise.reject(new ApiHandledError(errorMessage)); } return this.getData(response as SuccessApiResponse); } catch (error) { if (await this.checkSessionExpired(error.message)) { await this.redirectToLogin(); return new Promise(() => {}); } return Promise.reject(error); } }
方案对比
- 方案一:更规范,符合Promise错误处理逻辑,无潜在内存泄漏风险,推荐使用。
- 方案二:实现简单,但永久pending的Promise可能在极端场景下导致内存泄漏,仅适合快速临时解决问题。
内容的提问来源于stack exchange,提问作者Bernat
相关产品推荐
相关产品推荐

