在前端开发或者Node.js后端开发中,我们经常会遇到需要执行多个关联操作,并且要求这些操作要么全部成功,要么全部失败回滚的场景,这就是事务处理的核心需求。那么如何用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