You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:24:48