多异步Fetch请求致函数执行挂起问题排查
问题
开发注册组件时,流程为:
- 调用
uploadUser函数将用户数据插入数据库,成功返回用户ID,失败返回false; - 若ID存在,将图片上传至Firebase获取URL,再调用
upload-user-image接口结合ID更新数据库中的用户URL。
但navigate("/email-approval")始终未执行,浏览器网络面板显示upload-user-image请求已完成图片上传,但请求状态长期处于pending甚至永久pending,异步操作停滞。
相关代码如下:
const uploadUser = async () => { setDisableBtn(true); const user = { firstName: userData.firstName, lastName: userData.lastName, email: `${userData.email}@bushnaq.group`, password: userData.password, gender: userData.gender, birthDate: userData.birthDate, image: "placeholder", }; const res = await fetch("http://localhost:8000/api/signup", { method: "POST", body: JSON.stringify(user), headers: { "Content-Type": "application/json" }, }); const data = await res.json(); if (data.status === 400) { setDisableBtn(false); setErrSignup(data.message); return false; } return data.id; }; const handleSubmit = async (e) => { e.preventDefault(); let errors = {}; if ( userData.image.type !== "image/jpeg" && userData.image.type !== "image/png" ) { errors.errImg = "Profile picture must have JPEG or PNG extensions."; } if ( userData.firstName.length > 15 || userData.lastName.length > 15 || userData.firstName.length < 3 || userData.lastName.length < 3 ) { errors.errName = "First name and/or must be between 3 and 15 characters in length."; } if (userData.email.length > 25) { errors.errEmail = "Email is too long."; } if (userData.email.includes("@")) { errors.errEmail = "Email must not contain @ sign."; } if (!isPasswordStrong(userData.password)) { errors.errPs = "Please use a password that contains at least one number, one uppercase letter and at least 8 chatacters in length."; } if (userData.password !== userData.confirmPassword) { errors.errCp = "Passwords do not match."; } if (!userData.gender) { errors.errGender = "Gender must not be empty."; } setErrors(errors); if (!Object.keys(errors).length) { const id = await uploadUser(); if (id) { const imageRef = storageRef(storage, `images/${Date.now()}`); const snapshot = await uploadBytes(imageRef, userData.image); const url = await getDownloadURL(snapshot.ref); const res = await fetch("http://localhost:8000/api/upload-user-image", { method: "PUT", body: JSON.stringify({ id: id, url: url, }), headers: { "Content-Type": "application/json" }, }); const data = await res.json(); if (data.status === 400) { setErrSignup(data.message); } else { console.log("done"); navigate("/email-approval"); } } } };
问题分析与解决方案
1. 后端接口未返回响应
网络请求pending的核心原因大概率是后端upload-user-image接口未正确发送HTTP响应。比如后端完成数据库更新后,遗漏了res.send()/res.json()这类响应语句,导致前端的fetch一直处于等待状态,代码卡在await res.json()这一步,无法执行后续的else分支。
- 检查后端
upload-user-image接口代码,确保无论处理成功还是失败,都返回明确的响应。比如成功时返回:
失败时返回对应错误状态码和信息。res.json({ status: 200, message: "用户图片URL更新成功" });
2. 缺失错误捕获逻辑
当前代码未对fetch请求做异常捕获,如果upload-user-image请求出现网络错误(如后端崩溃、跨域问题),await会直接抛出异常,导致代码中断,无法走到else分支。
- 给
upload-user-image的请求添加try/catch,并检查响应是否正常:try { const res = await fetch("http://localhost:8000/api/upload-user-image", { method: "PUT", body: JSON.stringify({ id, url }), headers: { "Content-Type": "application/json" }, }); // 先判断响应状态是否正常 if (!res.ok) throw new Error(`请求失败,状态码:${res.status}`); const data = await res.json(); if (data.status === 400) { setErrSignup(data.message); } else { console.log("done"); navigate("/email-approval"); } } catch (err) { console.error("更新用户图片URL失败:", err); setErrSignup("图片信息更新失败,请重试"); setDisableBtn(false); }
3. uploadUser函数的异常处理缺失
uploadUser中的fetch同样没有错误捕获,如果注册请求出现网络错误,会直接抛出异常,导致handleSubmit后续逻辑中断。
- 给
uploadUser添加try/catch:const uploadUser = async () => { setDisableBtn(true); try { const user = { firstName: userData.firstName, lastName: userData.lastName, email: `${userData.email}@bushnaq.group`, password: userData.password, gender: userData.gender, birthDate: userData.birthDate, image: "placeholder", }; const res = await fetch("http://localhost:8000/api/signup", { method: "POST", body: JSON.stringify(user), headers: { "Content-Type": "application/json" }, }); if (!res.ok) throw new Error(`注册请求失败,状态码:${res.status}`); const data = await res.json(); if (data.status === 400) { setDisableBtn(false); setErrSignup(data.message); return false; } return data.id; } catch (err) { console.error("注册失败:", err); setDisableBtn(false); setErrSignup("注册失败,请检查网络或输入信息"); return false; } };
4. Firebase操作的潜在问题
虽然Firebase的uploadBytes和getDownloadURL显示完成,但如果存在未释放的资源或未处理的异步任务,也可能间接影响后续请求。不过结合现象来看,主要问题还是集中在后端接口响应和前端错误捕获上。
内容的提问来源于stack exchange,提问作者Aasem Shoshari
相关产品推荐
相关产品推荐

