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

React Native中tx.executeSql异步函数循环变量异常问题求助

问题分析与解决方案

你遇到的是闭包陷阱导致的异步回调变量捕获问题,同时代码里的await也没有真正等待所有数据库插入操作完成,我来一步步帮你解决:

核心问题:循环变量的闭包陷阱

for...in循环中的变量i你甚至没加var/let/const声明,会变成全局变量,本质上它是在循环外部作用域声明的,当异步的tx.executeSql回调执行时,循环已经全部跑完,i已经指向数组的最后一个索引3,所以所有回调里拿到的都是同一个i值。

另外,db.transaction本身不返回Promise,你用await它其实不会等待内部所有executeSql完成,这也是为什么控制台里"事务结束"、"insertarDatos执行完毕"会在回调打印之前出现。

解决方案

方案1:使用for...of循环(推荐)

for...of会为每次迭代创建独立的块级作用域,确保回调能捕获到当前迭代的正确索引/值,同时我们把transaction包装成Promise,让await能真正等待所有操作完成:

insertarDatos = async () => {
  console.log('开始执行insertarDatos');
  // 把transaction包装成Promise,让await能等待所有操作完成
  await new Promise((resolve, reject) => {
    db.transaction((tx) => {
      console.log('开始事务');
      // 用for...of遍历数组,拿到每个元素和索引
      for (const [i, usuario] of this.state.dataUsuarios.entries()) {
        // 改用参数化SQL,避免注入风险
        const sql = `INSERT INTO usuarios VALUES (?, ?, ?, ?, ?, ?)`;
        console.log('执行前i的值为: ' + i);
        tx.executeSql(sql, [usuario.id, usuario.nombre, usuario.usuario, usuario.password, usuario.grupo, usuario.faenas], (tx, results) => {
          console.log('executeSql回调中i的值为: ' + i);
          if (results.rowsAffected > 0) {
            console.log('数据已插入数据库!');
          } else {
            console.log('无法插入数据到数据库!');
          }
        }, (error) => {
          console.log(error.message);
          reject(error); // 捕获错误,终止Promise
        });
      }
      console.log('事务结束');
    }, (error) => {
      console.log('事务失败:', error.message);
      reject(error);
    }, () => {
      // 事务成功完成时resolve
      resolve();
    });
  });
  console.log('insertarDatos执行完毕');
};

方案2:使用Array.prototype.forEach

forEach的回调函数会为每个元素创建独立的作用域,天然避免闭包陷阱,同样搭配Promise包装来实现真正的等待:

insertarDatos = async () => {
  console.log('开始执行insertarDatos');
  await new Promise((resolve, reject) => {
    db.transaction((tx) => {
      console.log('开始事务');
      this.state.dataUsuarios.forEach((usuario, i) => {
        const sql = `INSERT INTO usuarios VALUES (?, ?, ?, ?, ?, ?)`;
        console.log('执行前i的值为: ' + i);
        tx.executeSql(sql, [usuario.id, usuario.nombre, usuario.usuario, usuario.password, usuario.grupo, usuario.faenas], (tx, results) => {
          console.log('executeSql回调中i的值为: ' + i);
          if (results.rowsAffected > 0) {
            console.log('数据已插入数据库!');
          } else {
            console.log('无法插入数据到数据库!');
          }
        }, (error) => {
          console.log(error.message);
          reject(error);
        });
      });
      console.log('事务结束');
    }, (error) => {
      console.log('事务失败:', error.message);
      reject(error);
    }, () => {
      resolve();
    });
  });
  console.log('insertarDatos执行完毕');
};

方案3:修复for...in的作用域问题(适合理解闭包原理)

在for...in里用let声明i,让每次循环都绑定当前的i值:

insertarDatos = async () => {
  console.log('开始执行insertarDatos');
  await new Promise((resolve, reject) => {
    db.transaction((tx) => {
      console.log('开始事务');
      // 用let声明i,每次循环绑定当前值
      for (let i in this.state.dataUsuarios) {
        const usuario = this.state.dataUsuarios[i];
        const sql = `INSERT INTO usuarios VALUES (?, ?, ?, ?, ?, ?)`;
        console.log('执行前i的值为: ' + i);
        tx.executeSql(sql, [usuario.id, usuario.nombre, usuario.usuario, usuario.password, usuario.grupo, usuario.faenas], (tx, results) => {
          console.log('executeSql回调中i的值为: ' + i);
          if (results.rowsAffected > 0) {
            console.log('数据已插入数据库!');
          } else {
            console.log('无法插入数据到数据库!');
          }
        }, (error) => {
          console.log(error.message);
          reject(error);
        });
      }
      console.log('事务结束');
    }, (error) => {
      console.log('事务失败:', error.message);
      reject(error);
    }, () => {
      resolve();
    });
  });
  console.log('insertarDatos执行完毕');
};

额外重要提醒:避免SQL注入

你原来的代码直接把变量拼接到SQL语句里,存在严重的SQL注入风险,上面的方案都改成了参数化查询,用?作为占位符,把变量放在第二个参数数组里,这样既安全又能避免字符串转义的问题。

内容的提问来源于stack exchange,提问作者Braven

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:45:52