Express+XMLHttpRequest数组增删响应异常及onload失效求助
嘿,我帮你排查出几个核心问题,都是异步处理逻辑不严谨和后端响应不规范导致的,咱们一步步来修复:
核心问题拆解
- 竞态条件导致判断失效:你原来的
saveData里,GET请求(检查重复)和后续的POST请求几乎是并行发起的,setTimeout只是侥幸缓解了问题,但根本没解决异步顺序问题——GET的响应还没回来,就可能已经执行了POST的判断逻辑。 - 后端未返回响应,导致前端请求挂起:你的POST和DELETE接口没有给前端返回任何响应,XMLHttpRequest的
onload事件只有在服务器返回完整响应后才会触发,这就是为什么有时候onload不执行的原因。 - 未处理请求错误:没给XHR添加错误处理,一旦网络波动或者后端报错,前端完全感知不到。
前端代码修复(HTML部分)
把XHR封装成Promise,让异步请求按顺序执行,去掉不靠谱的setTimeout,同时添加错误处理:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>User Manager</title> </head> <body> <input type="text" style="width: 1000px" id="input" placeholder="Enter username"> <button type="button" id="save">Save</button> <button type="button" id="delete">Delete</button> <script> // 封装XHR为Promise,方便异步顺序控制 function sendRequest(method, url, data = null) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(method, url); if (data) { xhr.setRequestHeader("Content-Type", "application/json"); } xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.responseText ? JSON.parse(xhr.responseText) : null); } else { reject(new Error(`Request failed with status ${xhr.status}`)); } }; xhr.onerror = () => reject(new Error("Network error occurred")); xhr.send(data ? JSON.stringify(data) : null); }); } async function saveData() { const inputValue = document.getElementById('input').value.trim(); if (!inputValue) { alert("Please enter a username!"); return; } try { // 先获取当前所有用户,确保拿到最新数据 const users = await sendRequest("GET", "http://localhost:4020/users"); // 检查是否重复 const isDuplicate = users.value.includes(inputValue); if (isDuplicate) { alert("This username already exists!"); return; } // 无重复则保存 await sendRequest("POST", "http://localhost:4020/users", { value: inputValue }); alert("User saved successfully!"); // 验证更新结果 const updatedUsers = await sendRequest("GET", "http://localhost:4020/users"); console.log("Updated users:", updatedUsers.value); } catch (err) { console.error("Save failed:", err); alert("Failed to save user."); } } async function deleteData() { const inputValue = document.getElementById('input').value.trim(); if (!inputValue) { alert("Please enter a username to delete!"); return; } try { // 先检查用户是否存在 const users = await sendRequest("GET", "http://localhost:4020/users"); if (!users.value.includes(inputValue)) { alert("User not found!"); return; } // 执行删除 await sendRequest("DELETE", "http://localhost:4020/users", { value: inputValue }); alert("User deleted successfully!"); // 验证更新结果 const updatedUsers = await sendRequest("GET", "http://localhost:4020/users"); console.log("Updated users:", updatedUsers.value); } catch (err) { console.error("Delete failed:", err); alert("Failed to delete user."); } } document.addEventListener('DOMContentLoaded', function () { document.querySelector("#save").addEventListener("click", saveData); document.querySelector("#delete").addEventListener("click", deleteData); }); </script> </body> </html>
后端代码修复
app.js(优化中间件顺序,添加错误处理)
const express = require('express'); const app = express(); // 全局解析JSON请求体,替代单独的body-parser app.use(express.json()); app.get('/', (req, res) => res.send('Hello World!')); app.use('/users', require('./routes/users')); // 404处理中间件 app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // 全局错误处理中间件 app.use(function(err, req, res, next) { res.status(err.status || 500); res.json({ error: err.message }); }); const port = process.env.PORT || 4020; app.listen(port, () => console.log(`Example app listening on port ${port}!`));
routes/users.js(关键:添加响应,处理边界情况)
var express = require('express'); var router = express.Router(); var array = []; router.get('/', (req, res) => { // 直接返回JSON,无需手动stringify res.json({ value: array }); }); router.post('/', (req, res) => { const username = req.body.value?.trim(); if (!username) { return res.status(400).json({ error: "Username is required" }); } if (array.includes(username)) { return res.status(409).json({ error: "Username already exists" }); } array.push(username); console.log("Updated users:", array); // 必须返回响应,告诉前端请求成功 res.status(201).json({ message: "User added successfully", value: array }); }); router.delete('/', (req, res) => { const username = req.body.value?.trim(); if (!username) { return res.status(400).json({ error: "Username to delete is required" }); } const index = array.indexOf(username); if (index === -1) { return res.status(404).json({ error: "User not found" }); } array.splice(index, 1); console.log("Updated users:", array); // 返回响应 res.json({ message: "User deleted successfully", value: array }); }); module.exports = router;
测试验证流程
按你的预期流程测试:
- 输入
joe点击Save → 数组变为[joe],前端弹出成功提示,控制台打印更新后的列表 - 输入
doe点击Save → 数组变为[joe, doe] - 输入
mary点击Save → 数组变为[joe, doe, mary] - 输入
doe点击Delete → 数组变为[joe, mary] - 重复输入已存在的用户名,会弹出重复提示;删除不存在的用户,会弹出找不到的提示
内容的提问来源于stack exchange,提问作者Isaac Michaan
相关产品推荐
相关产品推荐

