我写了这段代码来解决类似的问题。
我认为这是一种经典的数据库事务模型。它只实现了锁定机制,而没有回滚。它的工作方式如下:
- 您按照应该调用的顺序添加一堆同步方法
- 这些方法将在N毫秒内被调用
- 只会有一个请求获取DB数据(或任何其他需要修改的数据)和一个请求保存DB数据
- 每个方法都将接收到“最新数据”的引用,即处理程序№1更改的数据对象将由处理程序№2接收。顺序取决于№1的顺序
它适用于浏览器。它应该也适用于Node.js,但我没有测试过。
请注意,因为每个方法都将接收到对象的引用,所以您应该修改实际引用,如果您想读取它并返回某些值,则复制它,不要返回该引用,因为该引用的值可能会在未来被未来的处理程序更改。
TRANSACTION
将在执行之前等待N毫秒(默认为250)。这定义了哪些方法将分组为单个事务。您还可以进行即时调用。
以下是代码:
let TIMER_ID = 0;
let LOCK = Promise.resolve();
let RESOLVE_LOCK = undefined;
let FUNC_BUFFER = [];
let SUCCESS_BUFFER = [];
let FAIL_BUFFER = [];
async function get() {
return {
key1: "value1",
key2: {
key3: "value2"
}
};
}
async function set(value) {
return;
}
const TRANSACTION = {
add: async function(
f,
onSuccess,
onFail,
startTimeout
) {
await LOCK;
window.clearTimeout(TIMER_ID);
FUNC_BUFFER.push(f);
if (onSuccess) {
SUCCESS_BUFFER.push(onSuccess);
}
if (onFail) {
FAIL_BUFFER.push(onFail);
}
if (startTimeout == null) {
startTimeout = 250;
}
TIMER_ID = window.setTimeout(() => {
TRANSACTION.start();
}, startTimeout);
console.debug("Added in transaction");
},
start: async function() {
LOCK = new Promise((resolve) => {
RESOLVE_LOCK = resolve;
});
console.debug("Transaction locked");
let success = true;
try {
await TRANSACTION.run();
} catch (error) {
success = false;
console.error(error);
console.warn("Transaction failed");
}
if (success) {
for (const onSuccess of SUCCESS_BUFFER) {
try {
onSuccess();
} catch (error) {
console.error(error);
}
}
} else {
for (const onFail of FAIL_BUFFER) {
try {
onFail();
} catch (error) {
console.error(error);
}
}
}
FUNC_BUFFER = [];
SUCCESS_BUFFER = [];
FAIL_BUFFER = [];
RESOLVE_LOCK();
console.debug("Transaction unlocked");
},
run: async function() {
const data = await get();
const state = {
value1: data.key1,
value2: data.key2
};
for (const f of FUNC_BUFFER) {
console.debug("Transaction function started");
f(state);
console.debug("Transaction function ended");
}
await set({
key1: state.value1,
key2: state.value2
});
}
}
示例 № 1:
async function get() {
return {
key1: "value1",
key2: {
key3: "value2"
}
};
}
async function set(value) {
console.debug("Will be set:", value);
return;
}
new Promise(
(resolve) => {
TRANSACTION.add(
(data) => {
data.value2.key3 = "test1";
},
() => console.debug("success № 1")
);
TRANSACTION.add(
(data) => {
const copy = {
...data.value2
};
resolve(copy);
},
() => console.debug("success № 2")
);
TRANSACTION.add(
(data) => {
data.value1 = "test10";
data.value2.key3 = "test2";
},
() => console.debug("success № 3")
);
}
)
.then((value) => {
console.debug("result:", value);
});
示例2:
TRANSACTION.add(
() => {
console.log(1);
}
);
TRANSACTION.add(
() => {
console.log(2);
},
undefined,
undefined,
0
);
示例3:
TRANSACTION.add(
() => {
console.log(1);
}
);
TRANSACTION.add(
() => {
console.log(2);
},
undefined,
undefined,
0
);
await new Promise((resolve) => {
window.setTimeout(() => {
resolve();
}, 1000);
});
TRANSACTION.add(
() => {
console.log(3);
}
);