如何序列化导致大量异步处理的用户事件

时间:2019-03-19 21:34:42

标签: javascript asynchronous event-handling

我正在寻找一种模式来解决一个典型的问题,当多个(用户)事件彼此紧接着又被触发时,每个事件必须照顾一些繁重的负载,这些负载必须在新事件发生之前进行处理可以接受。

例如,典型的用例是用户可以单击的项目列表,并且对于每个项目,都需要异步加载和处理。 用户将单击将触发异步处理的项目。 处理事件时,选择另一个项目时,不应再进行新的处理,并且在最佳情况下(性能),仅需要处理最后一个事件。

以下代码片段模拟了用例,控制台输出显示了事件是如何发出的,然后进行了处理。 函数run模拟(用户)事件,函数heavyLifting模拟异步繁重的工作。 处理了比所需数量更多的事件,并且通过确保仅使用heavyLifting信号量依次调用currentlyLifting函数,也不再保证至少始终处理最后一个事件。

使用JSFiddle来运行已截断的代码

const getRandom = (min, max) => Math.floor(Math.random() * (max - min) + min);

const heavyLifting = id => {
return new Promise(resolve => {
	const cost = getRandom(500, 750)
	console.log(`heavy lifting "${id}" with cost "${cost}" started...`);
	setTimeout(() => {
		console.log(`heavy lifting "${id}" completed.`);
		resolve();
	}, cost);
});
};

let currentlyLifting;
window.addEventListener('heavyLift', async (e) => {
const id = e.detail.id;
if (currentlyLifting !== undefined) {
	console.log(`cannot start new heavy lifting of "${id}" while still running "${currentlyLifting}"`);
} else {
	currentlyLifting = id;
	await heavyLifting(id);
	currentlyLifting = undefined;
}
});

const run = () => {
let id = 1;
const timerCallback = () => {
	console.log(`dispatchEvent "${id}"`);
	window.dispatchEvent(new CustomEvent('heavyLift', {detail: {id}}));
	if (id++ < 10) {
		setTimeout(timerCallback, 100);
	}
};
timerCallback();
};

run();
<!DOCTYPE html>
<html>
	<body>
		<script src="index.js"></script>
	</body>
</html>

1 个答案:

答案 0 :(得分:0)

您可以在进行下一个处理之前等待上一个处理承诺:

 const oneAtATime = () => {
   let previous = Promise.resolve();
   return action => previous = previous.then(action);
 };

const lifting = oneAtATime();

lifting(async function() {
 //...
});

 lifting(async function() {
  //...
 });

或者等待刚刚完成的所有当前操作

 await lifting(); // don't do that in a lifting task itself, that will cause a livelock