问题描述
做添加购物车功能的时候,要求点击弹层中的"加入购物车"按钮时,
若未登录就先弹出二次确认框,
若已登录则调用添加购物车的封装api接口
代码如下:
// 点击弹层中的按钮:"加入购物车"
handleAddCart () {
if (!this.userInfo.token) { // token不存在:弹出提示框
// Dialog确认框
this.$dialog.confirm({
title: '温馨提示',
message: '此时需要先登录才能继续操作哦',
confirmButtomText: '去登录',
cancelButtonText: '再逛逛'
}).then(() => {
this.$router.replace({
path: '/login',
query: {
backUrl: this.$route.fullPath
}
})
}).catch(() => {
// 点击了取消按钮
this.showPannel = false
})
} else {//token存在:已登录
addCart(this.goodsId, this.addCount, this.goodsSkuId).then((res) => {
this.cartTotal = res.data.cartTotal
this.msg = res.message
console.log('获取添加购物车的cartTotal:', this.cartTotal)
this.$toast(this.msg)
})
this.addCartMethods()
this.$toast(this.msg)
this.showPannel = false
}
},
此处出现问题,即,在未登录状态下点击"加入购物车",二次确认框弹出之前,控制台会报错如下:
index.js??
clonedRuleSet-40.use[0]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!
./src/views/ProductDetail/index.vue?vue&type=script&lang=js:156
Uncaught (in promise)
解决方法:在if的最后添加return,确保代码不会往下执行
此时写入一个空的return并保存,vscode的插件(如 ESLint、Prettier 或 VSCode 的代码格式化工具)会自动删除 return 行
解决方法是
- 在
.eslintrc.js
中禁用no-useless-return
规则
module.exports = {
rules: {
'no-useless-return': 'off' // 允许显式 return
}
};
- 显式添加注释
if (!this.userInfo.token) {
this.showLoginDialog();
// eslint-disable-next-line no-useless-return
return; // 必须显式终止,避免后续代码执行
}
为什么return不能省略?
即使 token 不存在,代码仍会继续执行 else 块中的逻辑(addCart() 和 this.$ toast(this.msg)),导致:
-
发送无效的 addCart 请求(可能因未登录返回 401 错误)。
-
显示空的 this.$ toast(this.msg)(因为 this.msg 未被赋值)。
-
弹层被意外关闭(this.showPannel = false 被执行)。
为什么if条件已经成立的情况下仍然会执行else中调用异步接口的操作?
if-else 的分支选择是同步的,但 addCart() 是一个异步操作(Promise),
而 this.$ toast() 和 this.showPannel = false 是同步代码,
它们会在 addCart() 发起请求后立即执行,无论 if 条件是否成立。
期望中的业务流程为:
- 如果 token 不存在,执行 if 分支,显示弹窗并 return,后续代码不会执行
- 如果 token 存在,跳过 if 分支,直接执行 else 中的所有代码(包括 addCart() 和后续的同步代码)。
但实际上的业务流程为:
addCart() 返回一个 Promise,但 .then() 中的回调是异步的,不会阻塞后续同步代码。
因此,this.$ toast(this.msg) 和 this.showPannel = false 会在 addCart() 请求 刚发起时就立即执行,此时:
- this.msg 还未被赋值
- 弹层会被同步代码关闭
优化添加购物车的业务逻辑如下
方案一:将同步代码移入 .then()
if (!this.userInfo.token) {
// 未登录逻辑
this.$ dialog.confirm({ /* ... */ });
return;
}
// 已登录逻辑(无需 else,直接写后续代码)
addCart(this.goodsId, this.addCount, this.goodsSkuId)
.then((res) => {
this.cartTotal = res.data.cartTotal;
this.msg = res.message;
this.$ toast(this.msg); // 在异步回调中执行
this.addCartMethods();
this.showPannel = false; // 请求完成后关闭弹层
});
方案二:使用 async/await
async handleAddCart() {
if (!this.userInfo.token) {
await this.$ dialog.confirm({ /* ... */ });
return;
}
try {
const res = await addCart(this.goodsId, this.addCount, this.goodsSkuId);
this.cartTotal = res.data.cartTotal;
this.msg = res.message;
this.$ toast(this.msg);
this.addCartMethods();
} finally {
this.showPannel = false; // 确保无论成功失败都关闭弹层
}
}
总结:
避免在异步操作后直接写同步代码(除非你明确需要它们立即执行)。
使用 async/await 可以让异步逻辑更接近同步代码的阅读体验