为什么页面卡顿?从一个购物车例子说起
前几天朋友在做电商项目,用户一加购商品,页面就卡一下。点一次卡一次,体验很差。查了半天发现是每次点击都同步发请求,等服务器回了才让界面动。其实这就是典型的没用异步处理。
什么是客户端请求的异步处理
简单说,就是你发起一个网络请求时,不用干等着结果回来。程序可以继续做别的事,比如响应按钮点击、滚动页面、更新局部内容。等服务器返回数据后,再通过回调或事件机制去处理结果。
就像你点外卖,下单完不用一直盯着手机等送达。你可以该工作工作,饭到了自然会通知你。这就是异步思维。
常见实现方式:fetch + async/await
现代前端开发里,用 fetch 发请求已经很普遍。配合 async 和 await,代码写起来清晰又不容易出错。
async function addToCart(productId) {
try {
const response = await fetch('/api/cart/add', {
method: 'POST',
body: JSON.stringify({ productId }),
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
updateCartUI(result); // 更新购物车界面
} catch (error) {
console.error('添加失败:', error);
showErrorMessage('加入购物车失败');
}
}这个函数执行时不会阻塞主线程。用户点了“加入购物车”,界面立刻反馈,后台悄悄完成请求,完成后更新UI。
避免阻塞的关键:不占用主线程
浏览器只有一个主线程负责渲染和交互。如果用传统的 XMLHttpRequest 同步请求,或者在循环里大量发同步操作,页面就会冻结。
异步处理的核心优势就在于把耗时操作“甩出去”,让主线程保持灵敏。哪怕后端接口慢一点,用户也不会觉得卡。
实际场景中的优化点
有个细节很多人忽略:连续点击。用户手快点两下,结果发了两个重复请求,订单多了两条记录。这时候可以加个状态锁:
let isRequesting = false;
async function handleButtonClick() {
if (isRequesting) return; // 正在请求中,直接忽略
isRequesting = true;
try {
await addToCart(123);
showToast('已添加到购物车');
} finally {
isRequesting = false;
}
}这样即使连点十次,也只响应第一次,直到请求结束才放开锁定。
错误处理别忘了给用户反馈
异步请求失败时,不能只在控制台打个 log 就完事。用户不知道发生了什么,会觉得功能坏了。
正确的做法是捕获异常后,弹个提示,或者让按钮恢复可点击状态。比如网络超时,提示“网络不稳,请重试”;服务错误,记录日志同时告诉用户稍后再试。
异步不是万能药,但用好了能让产品体验上一个台阶。关键是理解它的运行逻辑,结合具体场景设计交互反馈。