如何用JavaScript处理事务?

来源:IPIPP.com作者:头衔:全栈工程师
导读:本期聚焦于小伙伴创作的《如何用JavaScript处理事务?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用JavaScript处理事务?》有用,将其分享出去将是对创作者最好的鼓励。

在前端开发或者Node.js后端开发中,我们经常会遇到需要执行多个关联操作,并且要求这些操作要么全部成功,要么全部失败回滚的场景,这就是事务处理的核心需求。那么如何用JavaScript处理事务呢?不同场景下的实现方式会有明显区别。

如何用JavaScript处理事务?

前端本地存储的事务处理

前端常用的本地存储有localStorage、IndexedDB,其中IndexedDB本身就支持事务机制,我们可以通过它来处理前端存储相关的事务。

比如我们需要同时更新两个对象存储的数据,保证两者要么都更新成功,要么都保持原状,就可以使用IndexedDB的事务:

// 打开数据库
const request = indexedDB.open('myDatabase', 1);

request.onupgradeneeded = function(event) {
  const db = event.target.result;
  // 创建两个对象存储
  if (!db.objectStoreNames.contains('user')) {
    db.createObjectStore('user', { keyPath: 'id' });
  }
  if (!db.objectStoreNames.contains('order')) {
    db.createObjectStore('order', { keyPath: 'id' });
  }
};

request.onsuccess = function(event) {
  const db = event.target.result;
  // 开启事务,指定操作的对象存储和读写模式
  const transaction = db.transaction(['user', 'order'], 'readwrite');
  
  transaction.oncomplete = function() {
    console.log('事务执行完成,所有操作都成功');
  };
  
  transaction.onerror = function(event) {
    console.log('事务执行失败,所有操作已回滚', event.target.error);
  };
  
  // 操作user对象存储
  const userStore = transaction.objectStore('user');
  userStore.put({ id: 1, name: '张三', age: 20 });
  
  // 操作order对象存储
  const orderStore = transaction.objectStore('order');
  orderStore.put({ id: 1001, userId: 1, amount: 200 });
};

Node.js后端数据库事务处理

在Node.js后端操作关系型数据库时,比如MySQL、PostgreSQL,我们可以通过数据库驱动提供的事务接口来处理事务。以MySQL为例,使用mysql2库可以实现标准的事务流程。

下面的代码示例演示了在Node.js中操作MySQL时如何处理事务,比如给用户转账的场景,需要同时扣减转出方余额和增加转入方余额:

const mysql = require('mysql2/promise');

async function transferMoney(fromId, toId, amount) {
  let connection;
  try {
    // 创建数据库连接
    connection = await mysql.createConnection({
      host: '127.0.0.1',
      user: 'root',
      password: 'password',
      database: 'test'
    });
    
    // 开启事务
    await connection.beginTransaction();
    
    // 扣减转出方余额
    const [fromResult] = await connection.execute(
      'UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?',
      [amount, fromId, amount]
    );
    if (fromResult.affectedRows === 0) {
      throw new Error('转出方余额不足或账户不存在');
    }
    
    // 增加转入方余额
    const [toResult] = await connection.execute(
      'UPDATE account SET balance = balance + ? WHERE id = ?',
      [amount, toId]
    );
    if (toResult.affectedRows === 0) {
      throw new Error('转入方账户不存在');
    }
    
    // 提交事务
    await connection.commit();
    console.log('转账事务执行成功');
    return true;
  } catch (error) {
    // 出错时回滚事务
    if (connection) {
      await connection.rollback();
    }
    console.log('转账事务执行失败,已回滚', error.message);
    return false;
  } finally {
    // 关闭连接
    if (connection) {
      await connection.end();
    }
  }
}

// 调用转账函数
transferMoney(1, 2, 100);

自定义异步操作的事务封装

如果我们需要处理多个自定义异步操作的事务,比如调用多个接口,要求全部成功才算完成,部分失败则回滚已执行的操作,可以自己封装事务逻辑。

下面的代码实现了一个简单的异步事务管理器,支持添加多个异步任务,出错时执行对应的回滚操作:

class AsyncTransaction {
  constructor() {
    this.tasks = []; // 存储任务{exec: 执行函数, rollback: 回滚函数}
  }
  
  // 添加任务
  addTask(execFn, rollbackFn) {
    this.tasks.push({
      exec: execFn,
      rollback: rollbackFn
    });
  }
  
  // 执行事务
  async run() {
    const executedTasks = [];
    try {
      for (const task of this.tasks) {
        await task.exec();
        executedTasks.push(task);
      }
      console.log('所有任务执行完成,事务成功');
      return true;
    } catch (error) {
      console.log('任务执行失败,开始回滚', error.message);
      // 逆序回滚已执行的任务
      for (let i = executedTasks.length - 1; i >= 0; i--) {
        try {
          await executedTasks[i].rollback();
        } catch (rollbackError) {
          console.log('回滚失败', rollbackError.message);
        }
      }
      console.log('事务回滚完成');
      return false;
    }
  }
}

// 使用示例
const transaction = new AsyncTransaction();

// 添加第一个任务:创建用户
transaction.addTask(
  async () => {
    console.log('创建用户');
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 100));
  },
  async () => {
    console.log('回滚用户创建,删除用户');
  }
);

// 添加第二个任务:创建订单
transaction.addTask(
  async () => {
    console.log('创建订单');
    await new Promise(resolve => setTimeout(resolve, 100));
    throw new Error('订单创建失败'); // 模拟任务失败
  },
  async () => {
    console.log('回滚订单创建,删除订单');
  }
);

// 执行事务
transaction.run();

事务处理的注意事项

  • 事务的范围要尽可能小,只包含必要的操作,避免长时间占用资源。
  • 事务中的操作要尽量幂等,避免重复执行导致数据异常。
  • 后端数据库事务要注意数据库的隔离级别,根据业务需求选择合适的隔离级别。
  • 自定义事务回滚时要考虑回滚操作本身也可能失败,需要做好异常处理。

总的来说,JavaScript处理事务的核心思路是保证一组操作的原子性,根据具体的使用场景选择对应的实现方式,就可以满足大部分业务的事务处理需求。

JavaScript事务处理异步编程数据库事务修改时间:2026-05-29 22:50:15

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。