
Next.js 应用中使用 Axios 发起请求时,若未正确 await Promise,会导致 UI 状态(如 loading)提前结束,而实际业务逻辑(如登录跳转、错误提示)却延迟执行,造成用户体验异常。根本原因在于 axios.post() 返回 Promise 但未被等待。
next.js 应用中使用 axios 发起请求时,若未正确 `await` promise,会导致 ui 状态(如 loading)提前结束,而实际业务逻辑(如登录跳转、错误提示)却延迟执行,造成用户体验异常。根本原因在于 `axios.post()` 返回 promise 但未被等待。
在您提供的代码中,login 函数看似是 async 的,但其内部调用了 axios.post(...).then().catch() 链式写法,并未用 await 暂停函数执行流。这使得 login() 函数在发起请求后立即返回一个已 resolve 的 Promise(等价于 Promise.resolve(undefined)),导致外层 submitForm 中的 finally { setLoading(false) } 过早执行——此时请求可能仍在网络中,then 或 catch 还未运行,自然无法触发 mutate()、错误设置或页面跳转等关键动作。
✅ 正确做法:统一使用 await + try/catch
将 login 函数改写为真正等待 Axios 请求完成的异步函数:
const login = async ({
email,
password,
remember,
setErrors,
setStatus
}) => {
await csrf(); // 确保 CSRF token 已获取
setErrors([]);
setStatus(null);
try {
const response = await axios.post('/login', {
email,
password,
remember
});
// 请求成功:触发 SWR / React Query 数据重验或手动跳转
mutate(); // 若使用 useSWR
// router.push('/dashboard'); // 或显式导航
} catch (error) {
if (error.response?.status === 422) {
setErrors(error.response.data.errors);
} else {
throw error; // 交由外层处理非表单验证错误(如网络失败、500)
}
}
};同时,保持 submitForm 的结构不变(它已正确使用 await login(...) 和 finally):
const submitForm = async (event) => {
event.preventDefault();
setLoading(true);
try {
await login({
email,
password,
remember: shouldRemember,
setErrors,
setStatus
});
// ✅ 此处可追加成功后逻辑(如 toast 提示、路由跳转)
} catch (error) {
// 可选:捕获并处理未被 login 内部吞掉的异常(如 CSRF 失败、网络中断)
console.error('Login failed:', error);
} finally {
setLoading(false); // ✅ 现在确保在请求完全结束后才关闭 loading
}
};⚠️ 关键注意事项
- 避免混用 .then().catch() 和 await:在同一请求链中选择一种风格。推荐全 await/try/catch,语义清晰、堆栈完整、错误可追溯。
- Axios 默认返回 Promise:axios.post() 本身不阻塞执行,必须 await 才能等待响应。
- mutate() 的时机很重要:它应在 axios.post() 成功 resolve 后调用,否则可能刷新过期或空数据。
- 错误分类处理:422 错误(表单验证失败)应由组件内 setErrors 处理;其他错误(如 500、超时、网络断开)建议抛出,由外层统一兜底(如全局错误边界或 toast)。
? 调试小技巧
可在 login 函数中添加日志辅助定位:
console.time('login-request');
const response = await axios.post('/login', {...});
console.timeEnd('login-request'); // 查看真实耗时结合浏览器 Network 面板确认:若控制台日志显示请求已完成,但 mutate() 仍未执行,则 99% 是 await 缺失所致。
遵循以上修正后,loading 状态将严格与请求生命周期对齐,用户操作反馈即时、逻辑执行可靠,彻底解决“按钮变回、但页面无反应”的典型异步陷阱。








