我有一个服务器,它同时接收请求,但是一些R/W资源不能同时访问,而且在数据库级别实现锁对我来说是不现实的,所以我想创建一个函数,将并行请求转换为顺序请求。
这个想法是让队列中的每个都有一个单独的资源id,当收到请求时,如果对应于资源id的队列为空,它就执行我想要的函数,如果不是,它就把要执行的函数排队,在其他函数完成之后执行。
我想出了一些代码是不工作的,我不明白为什么。
下面是小提琴的例子:
export const sleep = (ms: number): Promise<void> =>
new Promise((resolve) => { setTimeout(resolve, ms); });
type AsyncFn = () => Promise<void>;
const locks = new Map<string, AsyncFn[]>();
const unstackLock = async (id: string) => {
const stack = locks.get(id);
if (!stack) {
return;
}
const nextFn = stack.shift();
if (!nextFn) {
return;
}
try {
await nextFn();
} finally {
await unstackLock(id);
}
};
export const withLock = async (id: string, fn: AsyncFn): Promise<void> => {
if (!locks.has(id)) {
locks.set(id, []);
}
const lock = locks.get(id);
if (!lock) {
// never happens but makes TS happy
throw new Error('Lock is not defined');
}
lock.push(fn);
if (lock.length === 1) {
await unstackLock(id);
}
};
const test = async () => {
const results: number[] = [];
const f1 = withLock('lock', async () => {
await sleep(Math.random() * 100);
results.push(1);
});
const f2 = withLock('lock', async () => {
await sleep(Math.random() * 100);
results.push(2);
});
const f3 = withLock('lock', async () => {
await sleep(Math.random() * 100);
results.push(3);
});
await Promise.all([f1, f2, f3]);
if (results[0] !== 1 || results[1] !== 2 || results[2] !== 3) {
console.log('FAILED', results);
} else {
console.log('SUCCESS');
}
};
test();
我希望results
数组是[1, 2, 3]
,无论f1
、f2
和f3
(它们分别将1
、2
和3
推入results
数组)执行多长时间,但顺序是随机的。
1条答案
按热度按时间q3qa4bjr1#
好吧,我想到了一个可行的版本: