Node.js中forEach循环内用request模块无法更新JSON问题排查
问题排查:异步请求导致变量未更新的原因及修复方案
你的代码主要存在两个核心问题,都是Node.js异步编程中常见的坑:
1. 异步操作的时序问题
request.get是异步操作,它不会阻塞代码执行。当你的forEach循环遍历storedData.data时,只是发起了所有请求,但这些请求还没来得及返回结果,下面的console.log(updatedDataJSON)和res.end(JSON.stringify(updatedJSON))就已经执行了。这时候updatedDataJSON和updatedJSON都还是初始状态,自然看不到更新后的值。
简单说:你在请求完成前就去读取了还没被修改的变量。
2. 共享对象引用导致的覆盖问题
你在外部只定义了一个updatedDataJSON对象,每次forEach循环里修改的都是这个对象的属性,然后把它push到updatedJSON.data数组里。因为数组里存的是对象的引用,不是副本,所以最终数组里的所有元素都会变成最后一次循环修改后的结果,而不是每个请求对应的独立数据。
修复方案(使用async/await + Promise)
我们可以用Promise来管理异步操作,结合async/await让代码逻辑更直观,同时解决对象引用的问题:
步骤1:将request转换为Promise版本
原生request是回调风格的,我们可以用util.promisify把它封装成Promise,方便用await等待:
const request = require('request'); const { promisify } = require('util'); // 封装request.get为Promise版本 const getHtml = promisify((url, callback) => { request.get(url, (err, res, body) => { if (err) callback(err); else callback(null, body); }); });
步骤2:重写路由处理逻辑
把路由函数改成async函数,用Promise.all等待所有请求完成,同时每次循环创建新的updatedDataJSON对象:
app.get('/getDifferences', async (req, res) => { try { let updatedJSON = { data: [] }; // 用Promise版本的fs.readFile读取文件 const fileData = await fs.promises.readFile('output.json', 'utf-8'); const storedData = fileData ? JSON.parse(fileData) : null; if (storedData) { // 用map生成所有请求的Promise数组 const requestPromises = storedData.data.map(async (item) => { // 每次循环创建新的对象,避免引用共享 const updatedDataJSON = { url: item.url, dta: item.dta, status: "unchanged" }; try { const html = await getHtml(item.url); // 对比body内容 if (item.dta) { const storedBody = item.dta.match(/<body[^>]*>[\s\S]*<\/body>/gi)[0]; const currentBody = html.match(/<body[^>]*>[\s\S]*<\/body>/gi)[0]; if (storedBody !== currentBody) { updatedDataJSON.status = "changed"; updatedDataJSON.dta = html; } } } catch (err) { // 处理请求失败的情况 updatedDataJSON.status = "error"; updatedDataJSON.errorMsg = err.message; } return updatedDataJSON; }); // 等待所有请求完成,获取所有结果 updatedJSON.data = await Promise.all(requestPromises); console.log(updatedJSON.data); // 现在能拿到正确的更新后数据 } // 返回正确的响应 res.json(updatedJSON); } catch (err) { // 处理文件读取等错误 res.status(500).json({ error: '服务器错误', detail: err.message }); } });
关键修改点说明
- 异步时序控制:用
await等待文件读取和每个请求完成,Promise.all确保所有请求都处理完后再继续执行后续代码。 - 避免对象引用共享:每次循环创建新的
updatedDataJSON对象,保证数组里的每个元素都是独立的。 - 错误处理:添加了
try/catch捕获文件读取和请求中的错误,避免程序崩溃,同时返回友好的错误响应。
内容的提问来源于stack exchange,提问作者Vicky
相关产品推荐
相关产品推荐

