JavaScript async/await调用接口未等前序请求完成执行后续请求问题
问题原因
- 服务端数据库操作是异步回调逻辑,当前代码没有等待数据库插入、查询ID的操作完成就提前返回了响应:你在
con.query的回调函数外直接判断successful并返回响应,此时插入Rabbit表、查询最大ID的异步操作还没执行完,客户端拿到200响应时lastInsertedRId还没被赋值。 - 用全局变量存储
lastInsertedRId存在严重的并发问题:如果同时有多个用户提交请求,不同请求的ID会互相覆盖,完全无法保证外键关联正确性。 - 存在冗余查询风险:插入自增ID后不需要手动查询
max(R_ID),数据库插入结果里直接返回了最新插入的自增ID,直接取即可,查询最大值的逻辑在高并发场景下也可能拿到别的请求插入的ID。
解决方案
第一步:改造服务端/updateRtable接口
- 将响应返回逻辑移到数据库操作的回调最内层,确保所有操作完成后再返回
- 直接取插入结果的
insertId获取最新ID,废弃查询max ID的逻辑 - 把新生成的rabbitID直接返回给客户端,不再用全局变量存储
改后的代码:
app.post("/updateRtable", (req, res) => { console.log("went inside rabbit"); console.log(req.body.RName); con.query(`INSERT INTO RABBIT(R_ID, RABBIT_NAME, DATE_OF_BIRTH, RABBIT_STATUS, SEX, BREED) VALUES (NULL, '${req.body.RName}', '${req.body.dob}', '${req.body.rStat}', '${req.body.sex}', '${req.body.breed}')`, function(err, result) { if (err) { console.log(err); return res.status(ERROR_STATUS_CODE).send({ success: false }); } // 直接取插入结果自带的自增ID,不需要额外查询 const lastInsertedRId = result.insertId; // 所有操作完成再返回,同时把ID返回给客户端 res.status(200).send({ success: true, rabbitId: lastInsertedRId }); }); });
第二步:改造服务端/updateMyStable接口
不再读取全局的lastInsertedRId,改为从请求参数中取客户端传递的rabbitId:
app.post("/updateMyStable", (req, res) => { console.log("went inside mystuff table"); // 从请求参数中取rabbitId,不要用全局变量 const rabbitId = req.body.rabbitId; con.query(`INSERT INTO MYSTUFF(R_ID, NOTES, Kibble, Carrier, Medication, Treats, Blankets, Toys, Drop_off_Date, Pick_up_Date) VALUES ('${rabbitId}', '${req.body.notes}', '${req.body.kibble}', '${req.body.carrier}', '${req.body.medic}', '${req.body.treats}', '${req.body.blankets}', '${req.body.toys}', '${req.body.dropD}', '${req.body.picKD}')`, function(err, result) { if (err) { console.log(err); return res.status(ERROR_STATUS_CODE).send("Server side error"); } res.status(200).send({ success: true }); }); });
第三步:改造客户端逻辑
第一个请求拿到返回的rabbitId后,添加到第二个请求的表单参数中再发送:
async function updateAllHelper(formDataParam) { // 等待第一个接口完成,拿到返回的rabbitId let updateRResult = await updateRabbitTable(formDataParam); if (!updateRResult.success) { console.log("更新Rabbit表失败"); return; } // 把rabbitId添加到表单参数中 formDataParam.append('rabbitId', updateRResult.rabbitId); // 再发起第二个请求 let updateMySResult = await updateMyStable(formDataParam); } // 下面两个请求函数原有逻辑不需要改动 async function updateRabbitTable(formDataParam) { let headers = new Headers(); const response = await fetch("/updateRtable", { method: "POST", body: formDataParam, headers }); const body = await response.json(); return body; } async function updateMyStable(formDataParam) { let headers = new Headers(); const response = await fetch("/updateMyStable", { method: "POST", body: formDataParam, headers }); const body = await response.json(); return body; }
额外优化建议
你当前的SQL写法存在SQL注入风险,建议改用参数化查询替换字符串拼接的写法,避免恶意参数破坏数据库。
内容的提问来源于stack exchange,提问作者Futuristic Gladiator
相关产品推荐
相关产品推荐

